Gson: skip node level for serialization - java

I have some json which contains fields with custom names, e.g.:
{
"user_id": 123,
"user_name": "John",
"field_with_custom_name_1": "value1",
"field_with_custom_name_2": "value2",
"field_with_custom_name_3": "value3"
}
For describe this json the next model was created:
public class UserData {
#SerializedName("user_id")
private int userId;
#SerializedName("user_name")
private String userName;
private Map<String, String> customFields;
}
But after serialization we have json with the next structure:
{
"user_id": 123,
"user_name": "John",
"customFields": {
"field_with_custom_name_1": "value1",
"field_with_custom_name_2": "value2",
"field_with_custom_name_3": "value3"
}
}
Can you suggest how to ignore "customFields" level in the result?

You can use the following annotation to exclude a field from serialization and deserialization:
#Expose (serialize = false, deserialize = false)

Related

Jackson to convert a json into a map of String and a list

I have a json like below
{
"department": [
{
"status": "active",
"count": "100"
},
{
"status": "active",
"count": "300"
}
],
"finance": [
{
"status": "inactive",
"count": "500"
},
{
"status": "active",
"count": "450"
}
]
}
My Object is like below
class CurrentInfo
{
private String status;
private int count;
//getters and setters
}
I wanted to convert this JSON to a Map<String,List<CurrentInfo>>. I can convert the individual nodes using the below
ObjectMapper mapper = new ObjectMapper();
CurrentInfo currentinfo = mapper.readValue(jsonString, CurrentInfo.class);
Is there a way I can convert the previously mentioned JSON directly into a Map<String,List<CurrentInfo>>
Yes you can, and according to JSON above count is String type not int
class CurrentInfo {
private String status;
private String count;
//getters and setters
}
The use TypeReference to convert json string to java object
Map<String, List<CurrentInfo>> currentInfo = mapper.readValue(
jsonString,
new TypeReference<Map<String, List<CurrentInfo>>>() {});

Unable Parse a JSON using Jackson

I have a mongo database call in my code. The response from the database is mapped using codehaus jackson.
Json:
[
{
"_id": "555",
"rates": 1,
"reviews": [
{
"author_name": "Instructor 9999",
"_authKey": "demo\\556",
"text": "asdfa",
"date": 551,
"_id": "5454-4920",
"title": "asdf",
"comments": []
}
],
"votedUsers": [
{
"mng\\39999": 4
}
],
"rating": 4
},
{
"_id": "45589",
"rates": 1,
"reviews": [
{
"author_name": "feef",
"_authKey": "ad\\ads",
"text": "Working perfect",
"date": 1498659163,
"_id": "asdas-319",
"title": "test",
"comments": []
}
],
"votedUsers": [
{
"abc\\bis#cdf.com": 4
}
],
"rating": 4
}
]
I have created the below DTO Stucture:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MaterialReviewsDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String _id;
private int rates;
private List<ReviewsDTO> reviews;
private List<VotedUsersDTO> votedUsers;
//List<TypeReference<HashMap<String, String>>> votedUsers;
private int rating;.
//Getter Setter
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class VotedUsersDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, String> votedUser;
//Getter Setter
}
Below is the code where I am firing the query:
List<MaterialReviewsDTO> materialReviewsDTOs = DBConnectionRealmByDBName
.find(query,
MaterialReviewsDTO.class,
CollectionNameConstant.REVIEWS_COLLECTION);
Problem is all the JSON is getting mapped in DTO except the below part:
"votedUsers" : [
{
"abc\\bis#cdf.com" : 4
}
]
VotedUserDTO is null in response. VotedUsers is a list of object containg data in key-value pair.
I am not mentioning ReviewsDTO as this is getting mapped perfectly. How can I map votedUsers part?
Note: I am using Spring for development.
Some of the observations from your JSON
1. Json should be designed with Fixed key and variable values in mind.
2. Since in above case both Key and values are variable, we can use Map
So final solution is
change from private List<VotedUsersDTO> votedUsers; to private List<Map<String, Integer>> votedUsers
private List<Map<String, String>> votedUsers;
Do not use the explicit votedUser DTO.
The votedUsers is expected to be a List of VotedUsersDTOs.
If you look at your VotedUsersDTO in the JSON:
{
"abc\\bis#cdf.com" : 4
}
This would imply there is a field abc\\bis#cdf.com where you want the value to be 4.
This doesn't comply with id or votedUser Map in the DTO definition.

Mapping JSON with multiple entries to an array

I have a JSON file with multiple entries that have same attribute names, but different attribute values, such as:
{
"name" : { "first" : "A", "last" : "B" },
"gender" : "MALE",
"married" : false,
"noOfChildren" : 2
},
{
"name" : { "first" : "C", "last" : "D" },
"gender" : "FEMALE",
"married" : true,
"noOfChildren" : 1
}
The class that it should be mapped is:
public class Human {
private Name name;
private String gender;
private int age;
<getter, setters etc>
}
EDIT:
Service code is :
List<Human> humans = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
humans= objectMapper.readValue(json, new TypeReference<List<Human>>(){});
} catch (IOException e) {
e.printStackTrace();
}
JSON is parsed from HTTP entity and with correct format and now I added the annotation ass suggested in the answers.
As you can see, they have some attributes in common, but differ in others, and I would like to map those common fields. Is it possible to map the JSON this way ? I have tried mapping JSON to a collection/list/array of JsonNodes, but I keep getting erros about deserialization, while mapping only one instance of JSON entry works just fine. Is there another way of doing this ?
Use
#JsonIgnoreProperties(ignoreUnknown = true)
public class Human {
private Name name;
private String gender;
// getters, settets, default constructor
}
Or if you are using Lombok then it will be
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class Human {
private Name name;
private String gender;
}
use
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
while deserializing json to POJO class.
The JSON you have provide in question will give following error, as it is not a valid one.
Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
Valid Json would be like this:
[
{
"name": {
"first": "A",
"last": "B"
},
"gender": "MALE",
"married": false,
"noOfChildren": 2
},
{
"name": {
"first": "C",
"last": "D"
},
"gender": "FEMALE",
"married": true,
"noOfChildren": 1
}
]

JSON generic collection deserialization

I've such DTO classes written in Java:
public class AnswersDto {
private String uuid;
private Set<AnswerDto> answers;
}
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
private T value;
}
class LocationAnswerDto extends AnswerDto<Location> {
}
class JobTitleAnswerDto extends AnswerDto<JobTitle> {
}
public enum AnswerType {
LOCATION,
JOB_TITLE,
}
class Location {
String text;
String placeId;
}
class JobTitle {
String id;
String name;
}
In my project there is Jackson library used for serialization and deserialization of JSONs.
How to configure AnswersDto (use special annotations) or AnswerDto (annotation as well) classes to be able to properly deserialize request with AnswersDto in its body, e.g.:
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "JOB_TITLE",
"value": {
"id": "1",
"name": "Developer"
}
}
]
}
Unfortunately Jackson by default maps value of AnswerDto object to LinkedHashMap instead of object of proper (Location or JobTitle) class type.
Should I write custom JsonDeserializer<AnswerDto> or configuration by use of #JsonTypeInfo and #JsonSubTypes could be enough?
To properly deserialize request with just one AnswerDto in form of
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"type": "LOCATION",
"value": {
"text": "Dublin",
"placeId": "121"
}
}
I'm using:
AnswerDto<Location> answerDto = objectMapper.readValue(jsonRequest, new TypeReference<AnswerDto<Location>>() {
});
without any other custom configuration.
I've resolved issue by using Jackson's custom annotations #JsonTypeInfo and #JsonSubTypes:
public class AnswerDto<T> {
private String uuid;
private AnswerType type;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = Location.class, name = AnswerType.Types.LOCATION),
#JsonSubTypes.Type(value = JobTitle.class, name = AnswerType.Types.JOB_TITLE)
})
private T value;
}
My suggestion is to make a separate interface for possible answer values and use #JsonTypeInfo on it. You can also drop type field from AnswerDto, AnswerType enum, and additional *AnswerDto classes becuse jackson will add type info for you. Like this
public class AnswerDto<T extends AnswerValue> {
private String uuid;
private T value;
}
#JsonTypeInfo(use = Id.CLASS, include = As.PROPERTY)
interface AnswerValue {}
class Location implements AnswerValue { /*..*/ }
class JobTitle implements AnswerValue { /*..*/ }
Resulting json will looks like this
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"answers": [
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"#class": "com.demo.Location",
"text": "Dublin",
"placeId": "121"
}
},
{
"uuid": "e82544ac-1cc7-4dbb-bd1d-bdbfe33dee73",
"value": {
"#class": "com.demo.JobTitle",
"id": "1",
"name": "Developer"
}
}
]
}
Which will be parsed using
AnswersDto answersDto = objectMapper.readValue(json, AnswersDto.class);
But this solution applies only in cases when you are a producer of json data and you do not have to think about backward compatibility.
In other cases you'll have to make custom desetializer for AnswersDto class.

Gson deserialization with customer model

I have a json in a specific form that I have to deserialize.
In order to do that, I thought at my very best library Gson, but I'm facing a problem here because I have some key that are dynamic.
I think you will understand with a good exemple.
Here is the json :
{
"institution1": {
"_id": "51cc7bdc544ddb3f94000002",
"announce": { },
"city": "Melun",
"coordinates": [
2.65106,
48.528976
],
"country": "France",
"created_at": "2013-06-27T17:52:28Z",
"name": "italien",
"state": "Seine et Marne",
"street": "Avenue Albert Moreau",
"updated_at": "2013-06-27T17:52:28Z"
},
"institution2": {
"_id": "51d1dfa8544ddb9157000001",
"announce": {
"announce1": {
"_id": "51d1e036544ddb9157000002",
"created_at": "2013-07-01T20:02:35Z",
"description": "description formule 1",
"institution_id": "51d1dfa8544ddb9157000001",
"title": "formule dejeune",
"type": "restoration",
"updated_at": "2013-07-01T20:02:35Z"
},
"announce2": {
"_id": "51d1e074544ddb9157000003",
"created_at": "2013-07-01T20:03:08Z",
"description": "description formule soir",
"institution_id": "51d1dfa8544ddb9157000001",
"title": "formule soiree",
"type": "restoration",
"updated_at": "2013-07-01T20:03:08Z"
}
},
"city": "Melun",
"coordinates": [
2.65106,
48.528976
],
"country": "France",
"created_at": "2013-07-01T19:59:36Z",
"name": "restaurant francais chez pierre",
"state": "Seine et Marne",
"street": "Avenue Albert Moreau",
"updated_at": "2013-07-01T19:59:36Z"
}
}
You can go here for a better view.
So, I created a class to do that, JsonModel.java
public class JsonModel
{
public HashMap<String, Institution> entries;
public static class Institution
{
public String _id;
public HashMap<String, Announce> announce;
public String city;
public String country;
public List<Double> coordinates;
public String created_at;
public String name;
public String state;
public String street;
public String updated_at;
}
public static class Announce
{
public String _id;
public String created_at;
public String description;
public String institution_id;
public String title;
public String type;
public String updated_at;
}
}
And then, I asked Gson to deserialize that with the following code :
JsonModel data = new Gson().fromJson(json, JsonModel.class);
As data is null I presume that it is not working the way I expected...
And I thought about using HashMap because I don't know in advance the key like institution1, institution2, ...
What do you think ? Can I do that ?
And please don't tell me to use bracket, I just dream to have those !
Thank you in advance !
EDIT :
I was able to make this thing work by adding a root object
{
"root":{ ..the json.. }
}
AND changing
public HashMap<String, Institution> entries;
by
public HashMap<String, Institution> root;
So, the problem is gson need to recognise the first element but in fact I will not be able to modify the json, is there a way to get it done differently ?
The use of a Map is perfect, but you can't use your JsonModel class, because with that class you're assuming that in your JSON you have an object that contains a field called "entries" that in turn represents a map, like this:
{
"entries": { the map here... }
}
And you don't have that, but your JSON represents directly a map (not an object with a field called entries that represents a map!). Namely, you have only this:
{ the map here... }
So you need to parse it accordingly, removing the class JsonModel and using directly a Map to deserialize...
You need to use a TypeToken to get the type of your Map, like this:
Type mapType = new TypeToken<Map<String, Institution>>() {}.getType();
And then use the method .fromJson() with that type, like this:
Map<String, Institution> map = gson.fromJson(json, mapType);

Categories