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
Related
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)
I have written some code which saves the contents of a List<Class> to a JSON file, which looks kinda like this:
{ "firstName": "Name", "lastName": "LastName", "Email": "Email" } { "firstName": "Name2", "lastName": "LastName2", "Email": "Email2" }
Now I'm trying to input this file into my program, which works but only the first JSON Object is being returned. This is my code:
ObjectMapper mapper = new ObjectMapper();
JsonNode readFile = mapper.readTree(new File("path/to/file.json"));
How can I read the full JSON file and how can I add the contents of it to the same List mentioned above?
Every tutorial etc. I stumble upon only explains this using a single object.
Thank you!
You can do this:
Create a user class like this:
public class User {
private String email;
private String firstName;
private String lastName;
// Setters and getters
}
Now you can do this:
String json = yourJson;
ObjectMapper mapper = new ObjectMapper();
User[] userArray = mapper.readValue(json, User[].class);
List<User> userList = Arrays.asList(mapper.readValue(json, User[].class));
I am facing with a IMHO a strange behaviour of GSON. Let's take the following example:
{
"Name": "emaborsa",
"Surname": null
}
and my POJO is:
public class User {
#SerializedName("Name")
private String name;
#SerializedName("Surname")
private String surname;
// getter and setter
}
I deserialize it using the following code:
Gson g = new Gson();
User user = g.fromJson(json, User.class);
The variable name is set with "emaborsa", the variable surname I expected it were set to null but there is a string "null " instead.
Is it the correct behaviour or am I missing something? I tried to google it but it is hard to find something related to String and null...
This worked fine for me, using your code as the basis:
package gsonexample3;
import com.google.gson.Gson;
import com.google.gson.annotations.*;
public class User {
public static void main(String[] args) {
Gson g = new Gson();
User user = g.fromJson(json, User.class);
}
#SerializedName("Name")
private String name;
#SerializedName("Surname")
private String surname;
private static String json = "{\"Name\": \"emaborsa\", \"Surname\": null}";
}
Looks like you have to specify that you want to serialize nulls.
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
Coming from - https://howtodoinjava.com/gson/serialize-null-values/
I am using #JsonIgnore property to ignore some attributes in pojo, but these fields are not ignore in json response after parsing json using Gson library please help.
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class RideInvite extends RideInviteOld implements Serializable
{
private static final long serialVersionUID = -3729010679180341959L;
private double newFare = -1;
#JsonIgnore
private long prefPickupDropId;
#JsonIgnore
private String pickupLandmark;
#JsonIgnore
private String dropLandmark;
}
using following code to parse
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
jsonText = gson.toJson(msgObject);
Response after parsing
{"newFare":-1.0,"prefPickupDropId":2,"savePickupDropPoints":false,"pickupDropBasedOnTraffic":true,"allowFareChange":true}
here prefPickupDropId and savePickupDropPoints are json ignored but still value attribute is present in json text
I can not use #Expose for fields because my project is build in such away that ignoring fields which are not required json ignore and same pojos are using for preparing http response. This was working fine earlier but recently I am facing this issue
thanks in advance
Approach1:
Instead of using com.fasterxml.jackson.annotation.JsonIgnore you should use
com.google.gson.annotations.Expose to achieve your requirement.
Here is working code snippet -
import java.io.Serializable;
import com.google.gson.annotations.Expose;
public class RideRecord implements Serializable {
#Expose
private double newFare = -1;
private long prefPickupDropId;
private String pickupLandmark;
private String dropLandmark;
public RideRecord(double newFare, long prefPickupDropId, String pickupLandmark, String dropLandmark) {
super();
this.newFare = newFare;
this.prefPickupDropId = prefPickupDropId;
this.pickupLandmark = pickupLandmark;
this.dropLandmark = dropLandmark;
}
}
use the following code to parse:
RideRecord msgObject = new RideRecord(-1.0, 2, "sfo", "powell bart station");
GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
Gson gson = builder.create();
String jsonText = gson.toJson(msgObject);
System.out.println(jsonText);
It will give output:
{"newFare":-1.0}
because only newFare is the field which is exposed.
you can play with the #Expose attribute to meet your requirements.
Approach2:
If you don't want to use #Expose then also you can achieve your
requirement by just creating Gson object as below -
RideRecord msgObject = new RideRecord(-1.0, 2, "sfo", "powell bart station");
GsonBuilder builder = new GsonBuilder();
builder.addSerializationExclusionStrategy(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
return fieldAttributes.getName().equals("prefPickupDropId") || fieldAttributes.getName().equals("pickupLandmark") || fieldAttributes.getName().equals("dropLandmark");
}
#Override
public boolean shouldSkipClass(Class<?> arg0) {
// TODO Auto-generated method stub
return false;
}
});
Gson gson = builder.create();
String jsonText = gson.toJson(msgObject);
System.out.println(jsonText);
In this case also you will get the same output :
{"newFare":-1.0}
You're using Jackson with GSON and those are too separate frameworks.
One of the approaches is to initialize Gson to exclude fields that do not have the expose annotation.
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Then mark every field you need to use with the #Expose annotation:
ex:
class MyClass {
#Expose public String name;
}
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);