Automatic deserialization from String to object for #RequestBody in Spring - java

I am having issues with serialization and as much as I have been looking around, I am not able to find a solution to this.
I have inside my #RestController something similar to this method for and endpoint:
public ResponseEntity<String>(#RequestBody RequestObject requestObject )
The RequestObject looks like this:
public class RequestObject {
private Driver driver;
private List<Tracks> tracks;
//constructors, getters and setters
}
public class Tracks {
private Enum1 name;
private List<Enum2> missions;
//constructors, getters and setters
}
So, the problem comes with the Listof Enum2. This is because what I receive from the JSON is not a list of strings, but rather a string, that I need to parse to convert to the values of Enum2
So Enum2 looks like this:
A,
B,
C;
And what I get from the request as a JSON is:
{
"driver" : {
"name" : "myname"
}
},
"tracks" : [
{
"name" : "madrid",
"missions" : "ABCCBA"
},
{
"name" : "barcelona",
"mission" : "CBBCA"
},
]
}
Thing is all deserialization works perfectly out of the box (including all nested enums) except for the List<Enum2> missions
I have managed to do a dirty trick, but I'd like to do it the proper way, which I understand would be to write some kind of deserializer that runs when the controller is fired and that does all that parsing and converting from the string to the list (this splitting code I do have it, but I don't know where to put it).
At the moment I am getting this
2021-09-13 21:02:34.924 WARN 99904 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.ArrayList<org.package.model.Enum2>` from String value (token `JsonToken.VALUE_STRING`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<org.model.Enum2>` from String value (token `JsonToken.VALUE_STRING`)
at [Source: (PushbackInputStream); line: 13, column: 26] (through reference chain: org.model.RequestObject["missions"]->java.util.ArrayList[0]-org.model.Track["missions"])]
Is there a way to do this at all?

I found the solution. I had to mark Track as follows:
#JsonDeserialize(using = TrackDeserializer.class)
public class Track
and the write the TrackDeserializer:
public class TrackDeserializer extends JsonDeserializer<Track>{
#Override
public Track deserialize(JsonParser j, DeserializationContext ctxt) throws IOException{
JsonNode jsonNode = j.getCodec().readTree(jsonParser);
String name = jsonNode.get("name").asText();
final List<Enum2> enum = parseEnums(jsonNode.get("mission").asText());
return new Track(name, enum);
}
And parseEnums does the splitting and conversion to list.
Now TrackDeserializer is called for every Track with the given JSON in the question.

Probably, the simplest solution without using any Jackson annotation would be to use a custom setter method:
private void setMissions(String value) {
this.missions = convert_value_to_list_of_enums;
}
Jackson will invoke this method with values: ABCCBA and CBBCA.

Related

Error : Could not write JSON: Class java.util.ArrayList not subtype of map - Serialiation to JSON in Spring Application

I'm getting an error while sending List<List<LinkedHashMap<String, MyDTO>>> , it throws JSON parse exception. I've seen the same question in StackOverflow but there is no proper answer or maybe bcz the question wasn't detailed enough.
Error : Could not write JSON: Class java.util.ArrayList not subtype of [map type; class java.util.LinkedHashMap, [simple type, class java.lang.String] -> [simple type, class com.MyDTO]];
Basically, what I want is to convert LinkedHashMap to JSON, but the order is getting changed (tried with both HashMap & LinkedHashMap - JSON treats both as Map and so order issue still persists). To fix it what I'm trying now is to store each map entry into a separate List (List<LinkedHashMap<String, MyDTO>) and then storing these lists inside a single list i.e. List<List<LinkedHashMap<String, MyDTO>>.
Screenshot -
Screenshot of java data which is not converting to JSON
Here, the key is a String object & value is a DTO file.
From the java side, everything is OK but facing issue in serialization using Jackson in the spring application
(response converted by #RestController) it throws the error.
I'm using a RESTful web service using Spring Framework where everything is fine except this part.
Pls, tell me how do I fix it and don't mention any links to some other questions that are unanswered or to learn the whole topic as I've searched everywhere for similar questions and still confused?
You should be using TypeReference to resolve this issue like this:
ObjectMapper m = new ObjectMapper();
List<List<LinkedHashMap<String, MyDTO>>> obj = new ArrayList<>();
LinkedHashMap<String, MyDTO> map = new LinkedHashMap<String, MyDTO>();
MyDTO myd = new MyDTO("abc");
map.put("a1", myd);
map.put("a2", new MyDTO("def"));
List<LinkedHashMap<String, MyDTO>> list = Arrays.asList(map);
obj.add(list);
// serialization succeeded
String json = m.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
// deserialization succeeded
ArrayList<ArrayList<LinkedHashMap<String, MyDTO>>> o = m.readValue(json, new TypeReference<ArrayList<ArrayList<LinkedHashMap<String, MyDTO>>>>(){});
System.out.println(o);
// output
[ [ {
"a1" : {
"name" : "abc"
},
"a2" : {
"name" : "def"
}
} ] ]
[[{a1=MyDTO [name=abc], a2=MyDTO [name=def]}]]
Sample DTO class:
public class MyDTO {
private String name;
public MyDTO() {}
public MyDTO(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "MyDTO [name=" + name + "]";
}
}

Deserialize embedded JSON-String as typed Object

Given these to models (using Lombok #Data for simplicity)
#Data
public class RootModel {
private Integer myRootProperty;
private SubModel mySubModel;
}
#Data
public class SubModel {
private Integer mySubProperty
}
and this JSON-String:
{
"myRootProperty" : 5,
"mySubModel" : "{ "mySubProperty" : 3 }"
}
Is it possible (via Jackson-Annotations) to directly deserialze the embedded JSON-String (which origins from a DB-Column) to its Java-POJO-Model?
Background is that we need a JSON-Formatted configuration in our DB and I want to handle it typesafe as soon as possible - ideally directly after deserialization.
Yes, it sure is. Just annotate with the #JsonProperty tag
#Data
public class RootModel {
#JsonProperty("myRootProperty")
private Integer myRootProperty;
#JsonProperty("mySubModel")
private SubModel mySubModel;
}
#Data
public class SubModel {
#JsonProperty("mySubProperty")
private Integer mySubProperty
}
Then use the object mapper to deserialise into the POJO
RootModel rootModel = objectMapper.readValue(jsonString, RootModel.class);
The problem lies with your Json string. If you take a look at your Json,
{
"myRootProperty" : 5,
"mySubModel" : "{ "mySubProperty" : 3 }"
}
The subnode is enclosed in "" like "{ "mySubProperty" : 3 }" which will be considered as it is a value.
Instead if your Json is like as specified below (Note:I only removed the double quotes) like { "mySubProperty" : 3 }, deserialization won't complain any more. Your typical Json string would look like specified below.
{
"myRootProperty" : 5,
"mySubModel" : { "mySubProperty" : 3 }
}
No there is no automated way to do that. You could try a custom deserializer. Otherwise read it as just string and convert that to Typed Object in another step.

Deserializing parent and child node data of json in a single java object

I am new to Java and I am facing problem converting json data to java object. Lets say my json format is
{
"totalSize" : 2,
"done" : true,
"Id" : "xyz",
"Name" : "P0000005239",
"child" : {
"Type" : "someType",
"Region" : "someRegion",
"Name" : "C001906"
},
"Address_1" : "Address_1",
"Address_2" : "Address_1"
}
If my java class structure is like this, deserialization is working
//Working class Structure
class Demo{
int total_Size;
boolean flag_done;
String ID;
String p_name;
Child child;
String Address1;
String Address2;
//getter and setter
}
But my class structure is(to which I am not able to map my json)
//Not Working
class Demo{
int total_Size;
boolean flag_done;
String ID;
String p_name;
String c_type;
String c_region;
String c_name;
String Address1;
String Address2;
//getter and setter
}
Error
Invocation of init method failed; nested exception is
com.google.gson.JsonParseException: The JsonDeserializer
com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#4b28f983 failed to
deserialized json object
How to create java object from json having all the data in single class(i.e parent and child node data in single class without declaring separate child class)
I am using GSON with #SerializedName annotation for converting json into java object. Please let me know if you need more detail.
Try using fasterxml jackson
For this purpose you need to pass additional info in JSON:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
include=JsonTypeInfo.As.PROPERTY, property="#type")
class Base {
...
}
Then on serialization it will add #type field:
objectMapper.registerSubtypes(
new NamedType(ConcreteAAdapter.class, "ConcreteA"),
new NamedType(ConcreteBAdapter.class, "ConcreteB"),
new NamedType(ConcreteCAdapter.class, "ConcreteC")
);
// note, that for lists you need to pass TypeReference explicitly
objectMapper.writerWithType(new TypeReference<List<Base>>() {})
.writeValueAsString(someList);
{
"#type" : "ConcreteA",
...
}
on deserialization it will be:
objectMapper.registerSubtypes(
new NamedType(ConcreteA.class, "ConcreteA"),
new NamedType(ConcreteB.class, "ConcreteB"),
new NamedType(ConcreteC.class, "ConcreteC")
);
objectMapper.readValue(....)
More here: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

Populate POJO with array (root JSON node) using Jackson

I'm using Jackson and RESTEasy to hook into an external API. The API mainly returns simple objects which I have managed to successfully populate into POJOs.
I'm hitting a problem where I get an array of objects back e.g.
[
{
"variable1": "someValue1",
"variable2": "someValue2",
"variable3": "someValue3"
}
{
"variable1": "someValue4",
"variable2": "someValue5",
"variable3": "someValue6"
}
{
"variable1": "someValue7",
"variable2": "someValue8",
"variable3": "someValue9"
}
]
I have 2 classes: one called VariableObject which looks like this:
public class VariableObject {
private String variable1;
private String variable2;
private String variable3;
}
and VariableResponse which looks like:
public class VariableResponse {
private List<VariableObject> variableObjects;
}
My client uses JAXRS Response class to read the entity into the class i.e
return response.readEntity(VariableResponse.class);
I get a stack trace which reads:
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of VariableResponse out of START_ARRAY token
I understand you can return these as a List of POJOs i.e List quite easily, but this is not what I want to do.
The question really is two parts:
a. Can I possibly populate the VariableResponse POJO using Jackson (some how) preferably without a customer deserialiser? Maybe some annotation exists (this would be ideal)?
b. Is there some way to detect if an Array is being retuned as the root JSON node in the response and then act accordingly?
Help greatly appreciated.
Your JSON is indeed an array of objects.
You can deserialize it with:
response.readEntity(new GenericType<List<VariableObject>>() {});
And then create a new instance of VariableResponse passing resulting List as a constructor parameter like this:
public class VariableResponse {
private final List<VariableObject> variableObjects;
public VariableResponse(List<VariableObject> variableObjects) {
this.variableObject = new ArrayList<>(variableObjects);
}
}
You might forget to add comma after each {..}. After correcting your JSON string, I converted it into ArrayList<VariableObject> using TypeReference and ObjectMapper.
sample code:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
...
TypeReference<ArrayList<VariableObject>> typeRef = new TypeReference<ArrayList<VariableObject>>() {};
ObjectMapper mapper = new ObjectMapper();
try {
ArrayList<VariableObject> data = mapper.readValue(jsonString, typeRef);
for (VariableObject var: data) {
System.out.println(var.getVariable1()+","+var.getVariable2()+","+var.getVariable3());
}
} catch (Exception e) {
System.out.println("There might be some issue with the JSON string");
}
output:
someValue1,someValue2,someValue3
someValue4,someValue5,someValue6
someValue7,someValue8,someValue9
If you prefer your own response type direct.
Try just extending ArrayList?
public VariableResponse extends ArrayList<VariableObject> {
}

"Dotting" in JSON using Gson on Android

I'm trying to parse a JSON feed using Gson in Android. I know the JSON is valid. I suspect that it is because the format is like this:
"Info":[
{
"Id":"",
"Name":"",
"Description":"",
"Date":""
}
In order to parse this I need to "dot" in. Ex: Info.Name
How can I do this in a serialized DTO?
#SerializedName("Name")
public String name;
#SerializedName("Description")
public String desc;
#SerializedName("Date")
public String date;
I tried to put "Info." in front of each serializedName but that didn't work either. I also know my JSON parsing method works properly, because it's used somewhere else with a different DTO. But in that parsing, I don't have to "dotting" issue.
Can anyone help?
EDIT: I have tried everything you guys posted, and nothing works. The error says:
The JsonDeserializer failed to deserialize json object {"Info":[{".......
SECOND EDIT:
I was able to get rid of the error, but now it returns null. Haha, getting pretty damn frustrated right about now!
I am assuming that the actual JSON you are intaking is valid because the example you provided is not. In your JSON example, you have "Info":[ but there is no outer object containing the "Info" property, which is a must. The valid JSON would be:
{
"Info": [
{
"Id":"",
"Name":"",
"Description":"",
"Date":"",
}
]
}
This is a JSON object that has a property "Info" which has a value that is a list of objects. This list of objects contains one object that has the properties "Id", "Name", "Description", and "Date", all of which have empty-string values.
Here is a quick tutorial on how to use GSON to parse a JSON feed such as the above JSON:
You will need a class to represent the items in the list:
public class InfoItem {
public String Id;
public String Name;
public String Description;
public String Date;
public InfoItem() {}
}
And one to represent the list of Items:
public class InfoItemList extends LinkedList<InfoItem> {
public InfoItemList() { super() };
}
This added complexity is because GSON cannot otherwise get the type of a generic collection from the class data.
And one to represent the overall JSON message:
public class InfoMessage {
public InfoItemList Info;
public InfoMessage() {};
}
And then just:
gson.fromJson(jsonString, InfoMessage.getClass());
If just de-serializing a collection:
Type listType = new TypeToken<List<InfoItem>>() {}.getType();
gson.fromJson(jsonString2, listType);
The Info object is a list because of the []. You have to use the following code to deserialze it:
EDIT:
public class Info {
// as in your question
public String name;
...
}
public class Data {
#SerializedName("Info")
public List<Info> info;
}
Then just use the data class to deserialize your json.

Categories