How to convert String to list in java8 using ObjectMapper? - java

I have a JSON String called primarySkillStr :
[
{
"id": 3,
"roleIds": [
2
],
"rating": 2
}
]
I try to map it to an object as follows:
primarySkillList = mapper.readValue(primarySkillStr,
new TypeReference<List<PrimarySkillDTO>>() {});
But when Iam converting this to a List then the roleIds List is null.
Am I doing something wrong, or is there any other way?
This is my DTO
public class PrimarySkillDTO {
private Integer id;
private Integer rating;
private List<Integer> roleIds;
private String name;
}
I have the following annotations in the PrimarySkillDTO class
#Data
#Builder
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)

The problem is that your JsonNaming annotation requires snake_case and you are not using it.
To solve it
remove the annotation #JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
or, rename the variable in the JSON String to role_ids

SnakeCaseStrategy will map roleIds <--> role_ids, The following codes work for me:
ObjectMapper objectMapper = new ObjectMapper();
TypeReference<List<TestClass>> typeRef = new TypeReference<List<TestClass>>() {};
List<TestClass> testList = objectMapper.readValue(testStringObject, typeRef);

Related

Is it possible to deserialize values missing from JSON to some default values?

As an example class:
#Getter #Setter
public static class SomeClass {
private String notNull;
private String nullSetEmpty;
private String notExists;
}
Deserialization of null values to empty is possible by overriding configuration, like:
String json = " {\"notNull\": \"a value\", \"nullSetEmpty\": null}";
ObjectMapper om = new ObjectMapper();
om.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
SomeClass sc = om.readValue(json, SomeClass.class);
System.out.print(om.writerWithDefaultPrettyPrinter().writeValueAsString(sc));
This produces:
{
"notNull" : "a value",
"nullSetEmpty" : "",
"notExists" : null
}
But how about this notExists. It is possible to add default value to each class having the problem but is there any generic way to do that like configOverride does so that Jackson handles that?
you can just define default value in your data class
#Getter
#Setter
public static class SomeClass {
private String notNull;
private String nullSetEmpty;
private String notExists = "default-value";
}

Gson equivalent to Jackson #JsonInclude(JsonInclude.Include.NON_NULL)

This is actually a follow up for this question Recreate DTO class without field property instead of having it null using Gson/Jackson and Spring Boot. I originally posted it trying to make it work with Gson but only able to do it with Jackson using the #JsonInclude(JsonInclude.Include.NON_NULL) but haven't been able to find an equivalent for this with Gson so I can keep that as the library for the project.
Tried using #Expose(serialise=false, deserialise=false) where I had the #JsonInclude annotation or set that field to null as thought Gson by default would ignore that, but it doesn't seem to do it.
Finally, I tried to remove the #Expose annotation completely from to see if Gson would ignore that but not working either.
Pasting it here the main pieces for the issue as well as keeping the extra details added to the original post.
#Service
public class CategoryQueryServiceImpl implements CategoryQueryService {
#Autowired
private CategoryRepository categoryRepository;
#Autowired
private ReportRepository reportRepository;
ObjectMapper mapper = new ObjectMapper();
#Override
public CategoryQueryDto getCategory(UUID id) throws JsonProcessingException {
if (categoryRepository.findById(id).isPresent()) {
Category category = categoryRepository.findById(id).get();
CategoryQueryDto categoryQueryDto = new CategoryQueryDto(category.getId(), category.getTitle());
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String converter = gson.toJson(categoryQueryDto);
categoryQueryDto = gson.fromJson(converter, CategoryQueryDto.class);
// Jackson
//String converter = mapper.writeValueAsString(categoryQueryDto);
//categoryQueryDto = mapper.readValue(converter, CategoryQueryDto.class);
return categoryQueryDto;
} else {
return null;
}
}
#AllArgsConstructor
#NoArgsConstructor
#Data
public class CategoryQueryDto {
#Expose()
private UUID id;
#Expose()
private String title;
// Jackson
// #JsonInclude(JsonInclude.Include.NON_NULL)
private List<ReportQueryDto> reports = null;
public CategoryQueryDto(UUID id, String title) {
this.id = id;
this.title = title;
}
}
If anyone has any other ideas on how to do this please.
Thank you very much.
DO NOT serialize null fields (This is the default behavior of Gson serialization)
Employee employeeObj = new Employee(1, "John", "Smith", null);
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.create();
System.out.println(gson.toJson(employeeObj));
Output:
{
"id": 1,
"firstName": "John",
"lastName": "Smith"
}
Serialize null fields (Custom Gson serialization with null values included in JSON output)
Employee employeeObj = new Employee(1, "John", "Smith", null);
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
.create();
System.out.println(gson.toJson(employeeObj));
Output:
{
"id": 1,
"firstName": "John",
"lastName": "Smith",
"emailId": null
}
It seems the default behaviour for Gson was not taking place as Spring Boot uses Jackson as the default serialisation library. Overwriting this by pasting the below line within the application.properties file has fixed the issue for me.
spring.mvc.converters.preferred-json-mapper=gson

Deserialize JSON array in a deserialized JSON array using Jackson

I have a JSON structured like:
{
"id" : "123",
"name" : [ {
"id" : "234",
"stuff" : [ {
"id" : "345",
"name" : "Bob"
}, {
"id" : "456",
"name" : "Sally"
} ]
} ]
}
I want to map to the following data structure:
Class01
#Getter
public class Class01{
private String id;
#JsonDeserialize(using = Class01HashMapDeserialize.class)
private ArrayList<Class02> name;
}
Class02
#Getter
public class Class02{
private String id;
private ArrayList<Class03> stuff;
}
Class03
#Getter
public class Class03{
private String id;
private String name;
}
In my main Method im using an ObjectMapper with objectMapper.readValue(jsonString,new TypeReference<ArrayList<Class02>>(){}) to map this JSON to my Class01. This Class successfully deserealizes the Class02-array into the name array.
When it comes to the second array I don't know how to further deserialize as I am not able to access the json text from the class02 stuff entry.
#Override
public ArrayList<Class02> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
ArrayList<Class02> ret = new ArrayList<Class02>();
ObjectCodec codec = parser.getCodec();
TreeNode classes02 = codec.readTree(parser);
if (classes02.isArray()) {
for (JsonNode class02 : (ArrayNode) classes02) {
if(classe02.get("stuff").isArray()){
ObjectMapper objectMapper = new ObjectMapper();
ArrayList<Class03> classes03 = objectMapper.readValue(class02.get("stuff").asText(), new TypeReference<ArrayList<Class03>>(){});
}
ret.add(new Class02(class02.get("id").asText(), classes03));
}
}
return ret;
}
Why did you put a #JsonDeserialize annotation ? Jackson shall be able to deserialize it just fine without any custom mapping:
#Getter
public class Class01{
private String id;
private ArrayList<Class02> name;
}
Also in a first pass, I would generate the getters/setters/constructor manually for the 3 classes. There may be issues with Lombok & Jackson that you may want to solve later once you made the first version of the code works (Can't make Jackson and Lombok work together)
And your reader shall be more like:
ObjectMapper objectMapper = new ObjectMapper();
String text = ... //Your JSon
Class01 class01 = objectMapper.readValue(text, Class01.class)

Using Jackson to convert to and from an object with nested JSON

I have an Entity class below with two String fields: name and description. The description field is to contain a raw JSON value e.g. { "abc": 123 }
#Getter
#Setter
public class Entity {
private String name;
#JsonRawValue
private String descriptionJson;
}
I've got simple test code below using Jackson to serialize and deserialize:
Entity ent = new Entity();
ent.setName("MyName");
ent.setDescriptionJson("{ \"abc\": 123 }");
// Convert Object to JSON string
String json = mapper.writeValueAsString(ent);
// Convert JSON string back to object
Entity ent2 = mapper.readValue(json, Entity.class);
When converting Object -> JSON the description string is nested because the #JsonRawValue is set:
{"name":"MyName","descriptionJson":{ "abc": 123 }}
However, when I call the Jackson mapper.readValue function to read the JSON string back into an entity object I get the exception:
com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
at [Source: (String)"{"name":"MyName","descriptionJson":{ "abc": 123 }}"; line: 1, column: 36] (through reference chain: com.test.Entity["descriptionJson"])
Given that the #JsonRawValue annotation exists, how would you recommend marshalling the created JSON string back into to Entity object? Is there another annotation I'm missing?
Thanks
#JsonRawValue is intended for serialization-side only, but in this problem you can do like this:
#Getter
#Setter
public class Entity {
private String name;
#JsonRawValue
private String descriptionJson;
#JsonProperty(value = "descriptionJson")
public void setDescriptionJsonRaw(JsonNode node) {
this.descriptionJson = node.toString();
}
}
This problem is repeated with
How can I include raw JSON in an object using Jackson?.
For one of my requirements I used field type as Map to store Json as it is. This way I was able to read the nested JSOn as Map and when I serialize object to JSON, it came up correctly. Below is the example.
Entity.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class Entity {
public int id=0;
public String itemName="";
public Map<String,String> owner=new HashMap<>();
}
Temp.java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Temp {
public static void main(String[] args){
ObjectMapper objectMapper= new ObjectMapper();
try {
Entity entity
=objectMapper.readValue(Temp.class.getResource("sample.json"), Entity.class);
System.out.println(entity);
String json=objectMapper.writeValueAsString(entity);
System.out.println(json);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sample.json
{
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
You can use ObjectMapper from Jackson 2 as follows:
ObjectMapper mapper = new ObjectMapper();
String jsonStr = "sample json string"; // populate this as required
MyClass obj = mapper.readValue(jsonStr,MyClass.class)
try escaping the curly braces in the description json's value.
#JsonRawValue is intended only sor serializatio from docs:
Marker annotation that indicates that the annotated method or field should be serialized by including literal String value of the property as is, without quoting of characters.
To solve your problem you can try
public class Entity {
#Getter
#Setter
private String name;
private String descriptionJson;
#JsonRawValue
public String getDescriptionJson() {
return descriptionJson;
}
public void setJson(JsonNode node) {
this.descriptionJson = node.toString();
}
}

Can not convert json to Model

I have json from url. I need Convert This json to Model
{
"someField": 3,
"datesField": ["2017-08-19",
"2017-08-20",
"2017-08-26",
"2018-12-30"]
}
I create models for mapping
#Data
#NoArgsConstructor
private class Response{
#JsonProperty("someField")
private int someField;
#JsonProperty("datesField")
private DatesField datesField;
}
#Data
#NoArgsConstructor
private class DatesField{
private String[] strings;
}
try convert
ObjectMapper mapper = new ObjectMapper();
Dates dates = mapper.readValue(forObject, Response.class);
I get error when try convert:
Can not deserialize instance of packeg.DatesField out of START_ARRAY
token
The json attributed is incorrect according to the model. There is no array of datesField type but an array of strings within the datesField object.
Your object json equivalent shall be:
{
"someField": 3,
"datesField": {
"strings":["2017-08-19",
"2017-08-20",
"2017-08-26",
"2018-12-30"]
}
}
Or the other way, if you need to adapt to the json response, change your model as suggested by #xenteros to:
#Data
#NoArgsConstructor
private class Response{
#JsonProperty("someField")
private int someField;
#JsonProperty("datesField")
private String[] datesField;
}
Also, note that the java code to map the response should be changed from:
Dates dates = mapper.readValue(forObject, Response.class);
to
Response response = mapper.readValue(forObject, Response.class);
{
"someField": 3,
"datesField": ["2017-08-19",
"2017-08-20",
"2017-08-26",
"2018-12-30"]
}
is equivalent to
#Data
#NoArgsConstructor
private class Response{
#JsonProperty("someField")
private int someField;
#JsonProperty("datesField")
private String[] datesField;
}
You should rather parse the following json:
{
"someField": 3,
"datesField": {
"strings":
["2017-08-19",
"2017-08-20",
"2017-08-26",
"2018-12-30"]
}
}

Categories