Could not read JSON: Can not construct instance from String value - java

I have pojo class Ticket
public class Ticket implements Serializable{
#JsonProperty("lineItemStatus")
private String revisionStatus;
public String getRevisionStatus() {
return revisionStatus;
}
public void setRevisionStatus(String revisionStatus) {
this.revisionStatus = revisionStatus;
}
public void setRevisionStatus(RevisionStatus revisionStatus) {
String status = "";
if (revisionStatus != null) {
switch (revisionStatus) {
case added: {
status = "New";
break;
}
case updated: {
status = "Modified";
break;
}
}
}
this.revisionStatus = status;
}
}
Also I have an enum
public enum RevisionStatus {
added {
#Override
public String getName() {
return this.name();
}
},
updated {
#Override
public String getName() {
return this.name();
}
}
public abstract String getName();
}
During GET request , I use setRevisionStatus(RevisionStatus revisionStatus) method and i get response like for example
{"lineItemStatus": "Modified"} which is fine
But problem occurs during PUT request . During PUT request, my requirement is that I should be able to send payloads like for {"lineItemStatus": "Modified"} or {"lineItemStatus": "New"} or {"lineItemStatus": "abc"} , meaning lineItemStatus should be able to accept any String value . I am using #RequestBody Ticket ticket for receiving the payload.
The debugger doesn't come inside the controller and fails at the payload step . How do I handle this error without making any changes inside Enum ?

You can do it by updating your enum:
public enum RevisionStatus {
added("New") {
#Override
public String getName() {
return this.name();
}
},
updated("Modified") {
#Override
public String getName() {
return this.name();
}
};
private String status;
private RevisionStatus(String status) {
this.status = status;
}
public abstract String getName();
/**
* #return the status
*/
public String getStatus() {
return status;
}
#JsonCreator
public static RevisionStatus fromValue(String text) {
if(StringUtils.isNoneBlank(text)) {
for (RevisionStatus b : RevisionStatus.values()) {
if (String.valueOf(b.toString()).equalsIgnoreCase(text.trim())) {
return b;
}
}
}
return null;
}
}
Here we have updated enum to value enum. And fromValue() method with #JsonCreator annotation will bind this String value with the Enum constant.
Additionally this way you won't need that switch cases during get method. As getStatus() method will give you the status value directly. And that will improve your code design also.
Just an additional point here, I don't see any need of this getName() method, as the placed where getName() is called, directly name() can be called. As that is also a public method.

Related

Selma - Bad custom field to field mapping

I have a project that uses Mapstrcut, i want to replace it by Selma, so in the below example i mapped AttachmentRequest to Attachment
public class Attachment {
private SocialReason socialReason;
public SocialReason getSocialReason() { return socialReason; }
public Attachment socialReason(SocialReason socialReason) { this.socialReason = socialReason; return this; }
}
public class SocialReason {
private String value;
public String getValue() { return value; }
public SocialReason value(String value) { this.value = value; return this; }
}
public class AttachmentRequest {
private String socialReason;
public String getSocialReason() { return socialReason; }
public AttachmentRequest socialReason(String socialReason) { this.socialReason = socialReason; return this; }
}
I tried with the bellow code, but i have this error
Bad custom field to field mapping: field out.getSocialReason().value
from destination bean Attachment has no setter !
--> Fix #Field({"socialreason","socialreason.value"})
#Mapper(withIgnoreMissing = IgnoreMissing.ALL)
public interface AttachmentCommandMapper {
#Maps(withCustomFields = { #Field({ "socialReason", "socialReason.value" }) })
Attachment attachmentRequestToAttachment(AttachmentRequest attachmentRequest);
}
Is setters required in the class for the mapping in Selma, because in mapstruct doesn't require setters.

Convert JSON to JAVA Object through play.data.Form

I have no idea what to do with this problem.
I have a JSON object to assist in value input through POST (using Play Framework) that structures like this:
{
"start_absolute": 1403185486254,
"end_absolute": 1403185486254,
"metrics": [
{
"name": "parts",
"tags": [
{
"key":"machine",
"value":"10"
}
],
"sampling":
{
"value": 1,
"unit": "minutes"
}
}
]
}
And in server side i try to process the info like this:
Form<QueryForm> queryForm = Form.form(QueryForm.class).bindFromRequest();
if (queryForm.hasErrors()) {
return badRequest(queryForm.errorsAsJson());
}
QueryForm queryInfo = queryForm.get();
the classes used to define the JSON object in java
public class QueryForm {
private Date start_absolute;
private Date end_absolute;
private List<MetricForm> metrics= Lists.newArrayList();
public Date getStart_absolute() {
return start_absolute;
}
public void setStart_absolute(long start_absolute) {
this.start_absolute = new Date(start_absolute);
}
public Date getEnd_absolute() {
return end_absolute;
}
public void setEnd_absolute(long end_absolute) {
this.end_absolute =new Date(end_absolute);
}
public List<MetricForm> getMetrics() {
return metrics;
}
public void setMetrics(List<MetricForm> metrics) {
this.metrics = metrics;
}
#Override
public String toString() {
return "QueryForm[start_absolute="+start_absolute+", end_absolute="+end_absolute+", metrics="+metrics+"]";
}
.
public class MetricForm {
private String name;
private List<Tag> tags= Lists.newArrayList();
private RelativeTimeForm sampling= new RelativeTimeForm(1,"milliseconds");
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Tag> getTags() {
return tags;
}
public void setTags(List<Tag> tags) {
this.tags.addAll(tags);
}
public RelativeTimeForm getSampling() {
return sampling;
}
public void setSampling(int val, String unit) {
this.sampling.setUnit(unit);
this.sampling.setVal(val);
}
#Override
public String toString() {
return "MetricForm[name="+name+", tags="+tags+",sampling="+sampling+"]";
}
.
public class Tag {
private String key;
private String value;
public Tag(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return String.format(
"Tag[key=%s, value='%s']",
key, value
);
}
}
.
public class RelativeTimeForm {
private int value;
private String unit;
public RelativeTimeForm (int val, String unit){
this.setValue(val);
this.setUnit(unit);
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
#Override
public String toString() {
return "RelativeTimeForm[value="+value+", unit="+unit+"]";
}
I'm not experienced working with play.data.Form but in previous ocasions with simpler objects (Using primitive data types and String) this worked fine but when i tried to POST to the play application (Header: Content-Type = application/json; charset=utf-8) it occurs an Internal Server Error:
play.api.Application$$anon$1: Execution exception [[InvalidPropertyException:
Invalid property 'metrics[0].tags[0]' of bean class [model.QueryForm]: Illegal
attempt to get property 'tags' threw exception; nested exception is
org.springframework.beans.NullValueInNestedPathException: Invalid property
'metrics[0].tags' of bean class [model.QueryForm]: Could not instatiate property
path:
java.lang:InstatiationException: [model.Tag]
Any one can help with this? I canĀ“t figure out what to do. Is it to complex of an object to convert from JSON? Do i need to add some JSON Annotations (not currently using them)?
I have no clue how to fix this
UPDATE: I narrowed it down to the Tag class. for some reason i'm not able to instatiate Tag through JSON. I even tried setting a single Tag instance in the same level as the dates and it gives the same error (It work for Sampling)
RESOLVED:
Spring binding exception when a form is submitted
The answer is in the post above. I have to set an empty construtor in Tag class for it to work.
public Tag(){
}
The exception means, that one or more attributes aren't instantiated.
private List<MetricForm> metrics = new ...;
private List<Tag> tags = new ...;
private RelativeTimeForm sampling = new ...;
should do it.
In the case you get problems to read in your time values: Instead of using Date type directly to read in, I would use long to read in the time values and then (internally) convert them to Date objects.

How do i create a string enum types without overriding toString method to be used in Mule Studio?

I'm working with Mule Studio in this, and I actually was wondering the possibility to add the drop down list with a "custom" string name. The case applies for MIME Types (ContentTypes) names. (i.e: text/css, text/javascript, and so)
Made an structure like this:
public enum HttpContentType {
TEXT_PLAIN
{
public String toString() {
return "text/plain";
}
},
TEXT_CSS
{
public String toString() {
return "text/css";
}
},
TEXT_JS
{
public String toString() {
return "text/javascript";
}
},
TEXT_XML
{
public String toString() {
return "text/xml";
}
},
};
To then be used like this as a Configurable attribute:
/**
* Connection Content Types
*/
#Configurable
#Placement(order=1,group="Configuration",tab="HTTP Configuration")
private HttpContentType contentType;
But of course that when reading that from the Mule Studio it will be a Drop Down list with names such as: TEXT_CSS, TEXT_XML, TEXT_JAVASCRIPT ( http://puu.sh/3vLbd.png ) and so instead of text/css, text/xml, text/javascript.
How can i achieve this result?
A far better way to achieve this would be to provide a field in your enum, and provide an appropriate constructor:
public enum HttpContentType {
TEXT_PLAIN("text/plain"),
TEXT_CSS("text/css");
private String value;
HttpContentType(String value) {
this.value = value;
}
public String toString() {
return this.value;
}
public static HttpContentType getByValue(String value){
for (final HttpContentType element : EnumSet.allOf(HttpContentType.class)) {
if (element.toString().equals(value)) {
return element;
}
}
return null;
}
}
One option is to provide a different method to get the MIME string.
public interface IMimeTypeProvider {
public String toMimeType();
}
public enum HttpContentType implements IMimeTypeProvider {
TEXT_PLAIN
{
public String toMimeType() {
return "text/plain";
}
},
...
public String toMimeType() {
return "text/plain";
}
}
You might also consider adding a toDisplayString() method. The enumerator name is not necessarily a good display name, even in a single locale.
public enum HttpContentType {
TEXT_PLAIN("text/plain"), TEXT_CSS("text/css");
private String code;
HttpContentType(String code) {
this.code = code;
}
public String toString() {
return code;
}
public static HttpContentType getEnum(String code) {
if (code.equals("text/plain")) {
return TEXT_PLAIN;
} else if (code.equals("text/css")) {
return TEXT_CSS;
} else {
return null;
}
}
}

Can I override the #RequestMapping on a type for a method?

I have a spring controller/POJO like this:
#RequestMapping("/foo");
public class MyController {
#RequestMapping("/bar")
public String MyAction() { return someSharedFunc(false); }
#RequestMapping("/debug/ping");
public String MyDebugPing() { return someSharedFunc(true); }
private String someSharedFunc(boolean debug) {
if(debug) return "FooBar"; else return "Debug!";
}
}
In this scenario, the URL for MyDebugPing is /foo/debug/ping. However, I want it to be /debug/ping, effectively ignoring the RequestMapping on the class.
Is that possible?
Just remove the #RequestMapping annotation from the class and use full paths per individual methods. E.g.
public class MyController {
#RequestMapping("/foo/bar")
public String MyAction() { return someSharedFunc(false); }
#RequestMapping("/debug/ping");
public String MyDebugPing() { return someSharedFunc(true); }
private String someSharedFunc(boolean debug) {
if(debug) return "FooBar"; else return "Debug!";
}
}
If there is a lot of methods then you can simply move out the method to another controller.

Struts 2 does not populate POJO from client

I have a Java Object with many other nested Objects and lists of Objects. When the request arrives from the client, I see that the Object is populated only to a few levels. Is there any configuration that sets this is Struts 2? Here is my example.
class MyActionClass extends ActionSupport {
private Abc abc;
public Abc getAbc() {
return abc;
}
public void setAbc(Abc abc) {
this.abc = abc;
}
public String populate() {
MyService myService = new MyService();
abc = myService.getMyAbc();
return SUCCESS;
}
public String update() {
MyService myService = new MyService();
myService.updateAbc(abc);
return SUCCESS;
}
}
class Abc {
private List<Def> defList;
private Ghi ghi;
public void setDefList(List<Def> defList) {
this.defList = defList;
}
public List<Def> getDefList(){
return defList;
}
public void setGhi(Ghi ghi) {
this.ghi = ghi;
}
public Ghi getGhi() {
return ghi;
}
}
class Def {
private String name;
private long id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
class Ghi {
private List<Def> defList;
private String ghiName;
public void setDefList(List<Def> defList) {
this.defList = defList;
}
public List<Def> getDefList() {
return defList;
}
public void setGhiName(String ghiName) {
this.ghiName = ghiName;
}
public String getGhiName() {
return ghiName;
}
}
When I call the populate method and when send to the jsp, the iteration happens good with all the elements. But, when I try to update, i.e. when then form is submitted, the update() method is called, but the instance variable abc does not get populated completely.
I have seen the url which is passed and everything seems to be fine. Let me tell you what happens. The url will be something like (splitting with line break for easy understanding here),
&abc.defList[0].name=alex
&abc.defList[0].id=1
&abc.defList[1].name=bobby
&abc.defList[1].id=2
&abc.ghi.ghiName=GHINAME
&abc.ghi.defList[0].name=Jack
&abc.ghi.defList[0].id=1
&abc.ghi.defList[1].name=Jill
&abc.ghi.defList[1].id=2
In this case, the defList inside abc and ghi.ghiName in abc are populated with no issues. But the defList of abc.ghi is not populated. Is this a common behavior of Struts 2? Is ther any means by which this can be overridden?
Got the issue solved. Struts 2 rock. Since the code I got was for a bug fix, did not know what was inside it, not even checked it once.
The culprit was the toString() method which was overridden. This had no check on the map for null and called the entrySet() method on it. This generated Exception and prevented Struts from populating the objects.
For better understanding, Struts do call the toString() method for some purpose while populating. If anyone faces this in future, do remember to check if you have overridden toString() and if everything is set inside it.

Categories

Resources