Gson deserialization with customer model - java

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);

Related

Deserializing complex json with matching objects by id (Jackson)

I have a proprietary API that return a complex JSON like:
{
"store": "store_name",
"address": "store_address",
"department": [
{
"name": "d1",
"type": "t1",
"items": [
"i1",
"i2"
]
},
{
"name": "d2",
"type": "t2",
"items": [
"i3"
]
}
],
"itemDescriptions": [
{
"id": "i1",
"description": "desc1"
},
{
"id": "i2",
"description": "desc2",
"innerItems": [
"i2"
]
},
{
"id": "i3",
"description": "desc3"
}
]
}
Is it possible to deserialize this JSON using Jackson into:
#AllArgsConstructor
class Store {
private final String store;
private final String address;
private final List<Department> departments;
/*some logic*/
}
#AllArgsConstructor
class Department {
private final String name;
private final String type;
private final List<Item> items;
/*some logic*/
}
#AllArgsConstructor
class Item {
private final String id;
private final String description;
private final List<Item> innerItems;
/*some logic*/
}
I tried to find answers, but find only this question without solution.
I know that I can do it in my code (deserialize as it is and create objects from result), but its very memory intensive (I have a lot of json and it can be large).
I know that I can write fully custom deserializer, but in this case, I have to describe the deserialization of each field myself - in case of some changes, I will have to change the deserializer, and not just the class(POJO/DTO).
Is there a way to do this with Jackson (or Gson) or with a minimal (preferably relatively generic) amount of my code?

Mapping a List inside a List using SerializedName

I have an object class, called Device, inside that there is a List of objects, which also contains a List of objects.
Model looks like this:
public class Device {
#SerializedName("tests")
public List<Test> tests;
public static class Test {
#SerializedName("id")
public String id;
#SerializedName("testInfo")
private List<TestInfo> testInfo = new ArrayList<TestInfo>();
}
public static class TestInfo {
#SerializedName("id")
private String id;
}
}
Now when it is mapping the request, it maps the first List, meaning when printing the result I get this:
Test{id='123', testInfo = []}
Test{id='124', testInfo = []}
The testInfo is always an empty list, even though the data is there. I tried it with and without the new ArrayList. Is the problem here that SerializedName does not know how to map a list inside a list or am I doing somthing wrong?
EDIT
Data that is being mapped:
"tests": [
{
"id": "123",
"testInfo": [
{
"id": "321",
},
{
"id": "322",
}
]
},
{
"id": "124",
"testInfo": [
{
"id": "421",
},
{
"id": "422",
}
]
},
]

Custom Gson serializer with fields that are unknown at runtime

I'm attempting to do custom Gson serialization to create a Json object to send to a service except their are some fields that are not known at runtime.
The json I wish to create should look something like this:
{
"type": "configuration/entityTypes/HCP",
"attributes": {
"FirstName": [
{
"type": "configuration/entityTypes/HCP/attributes/FirstName",
"value": "Michael"
}
]
},
"crosswalks": [
{
"type": "configuration/sources/AMA",
"value": "10000012"
}
]
}
I am able to successfully create this json using Gson, but the issue is that I have thousands of fields that could be under the attributes object, in this example there is only the FirstName but if I was doing a create there would be as many attributes as that person, place or thing had associated with them.
Because currently I am able to create this using Gson by having 4 different classes:
Type
Attributes
FirstName
Crosswalks
But I want to be able to have FirstName, LastName, MiddleName, etc. all underneath the attributes object without creating an individual java class for all of them. The json would look like this in that case:
{
"type": "configuration/entityTypes/HCP",
"attributes": {
"FirstName": [
{
"type": "configuration/entityTypes/HCP/attributes/FirstName",
"value": "Doe"
}
],
"LastName": [
{
"type": "configuration/entityTypes/HCP/attributes/LastName",
"value": "John"
}
],
"MiddleName": [
{
"type": "configuration/entityTypes/HCP/attributes/MiddleName",
"value": "Michael"
}
]
},
"crosswalks": [
{
"type": "configuration/sources/AMA",
"value": "10000012"
}
]
}
Is there a way to use Gson to create the attributes object without creating java objects for all of the different attributes I have?
You can use Map<String, Object> where Object will be an one-element-array. See, for example, below model:
class Attributes {
private Map<String, Object> attributes;
// getters, setters
}
class Type {
private final String type;
private final String value;
public Type(String type, String value) {
this.type = type;
this.value = value;
}
// getters
}
Now, let's build attributes manually:
import com.google.gson.Gson;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class GsonApp {
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("FirstName", Collections.singletonList(new Type("url/FirstName", "Rick")));
map.put("LastName", Collections.singletonList(new Type("url/LastName", "Pickle")));
Attributes attributes = new Attributes();
attributes.setAttributes(map);
String json = new Gson().newBuilder().setPrettyPrinting().create().toJson(attributes);
System.out.println(json);
}
}
Above code prints:
{
"attributes": {
"FirstName": [
{
"type": "url/FirstName",
"value": "Rick"
}
],
"LastName": [
{
"type": "url/LastName",
"value": "Pickle"
}
]
}
}

Parsing specific JSON with Gson

I'm currently trying to parse a JSON response with the following structure using Gson:
{
data {
"1": {
"name": "John Doe"
},
"2": {
"name": "John Doe"
},
...
}
My response class:
class Response {
Map<String, ModelObj> data;
}
and model class:
class ModelObj {
String name;
}
But what I can't figure out is how to simply map everything to a single List where the id is placed within the ModelObj without having them separate as key/value pairs in a Map. So ideally my response class would be:
class Response {
List<ModelObj> data;
}
and model class:
class ModelObj {
String id;
String name;
}
How would this be accomplished?
[
{
"id": 1,
"name": "John Doe"
},
{
"id": 2,
"name": "John Doe"
}
]
If your response is of above form then only you will get the list of model object and you will get Array of ModelObj
then use below code to convert to List
public <T> List<T> getObjectToList(String json, Class<T[]> ObjectArrayClass) {
return Arrays.asList(gson.fromJson(json, ObjectArrayClass));
}
List< ModelObj > arrayList = getObjectToList(jsonResponseString, ModelObj[].class);

I would like to iterate a big Json's properties and sub properties name with jackson

My final aim is to get all the properties name and its types from a local file path.
Example
{
"description": "Something",
"id": "abc.def.xyzjson#",
"type": "object",
"properties": {
"triggerTime": {
"type": "string",
"description": "Time of adjustment event",
"source": "ab.cd",
"pattern": "something",
"required": true
},
"customerId": {
"type": "string",
"description": "Something",
"source": "ef.gh",
"required": true
}, ..... many more properties
Under some properties, there are sub-properties and their Type.
I want final Output as-
triggerTime String
customerId String (also sub)
There are multiple solutions to your problem.
One would be what Sharon suggested with just iterating the big map.
Another solution however would be to create wrapper classes ( POJOs ) to work with, which also could provide other useful aspects depending on your scale and use case.
public class YourJsonObject {
private String description;
private string id;
private String object;
private JsonProperties properties;
public YourJsonObject() {
}
public JsonProperties getProperties(){
return properties;
}
public void setProperties(JsonProperties properties){
this.properties = properties;
}
public String getDescription(){
return description;
}
public void setDescription(String description) {
this.description= description;
}
public String getId(){
return id;
}
public void setId(String id){
this.id = id;
}
//and so on with the getter setter
}
//The class JsonProperties used in YourJsonObject
public class JsonProperties{
private TriggerTime triggertime;
private Customer customerId;
public JsonProperties() {
}
public TriggerTime getTriggertime(){
return triggertime;
}
//and so on
}
Then somewhere else you can just do this:
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
yourJsonObject example = new yourJsonObject(); // have your POJO you want to save
mapper.writeValue(new File("result.json"), example);
To read you can just use:
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
yourJsonObject value = mapper.readValue(new File("data.json"), yourJsonObject .class); // data.json is the json file you want to read. Else you can also pass a String into it with method overloading.
Both snippets are taken from my linked wiki article from jackson themselves.
Jackson should automatically be able to parse this POJO to an equivalent JSON if configured correctly. Note: Jackson has to be globally registered and has to know about it. Please read the wiki of what you use to know about it. Jackson in 5 Minutes
My advice is to load the json into Map<String, Object> and then to recursively iterate on the map and collect the data you require
Here is the entire solution for you
public class JsonProperties
{
#SuppressWarnings("unchecked")
public static void main(String[] args)
{
ObjectMapper mapper = new ObjectMapper();
try (InputStream is = new FileInputStream("C://Temp/xx.json")) {
Map<String, Object> map = mapper.readValue(is, Map.class);
List<Property> properties = getProperties((Map<String, Object>)map.get("properties"));
System.out.println(properties);
} catch (IOException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
public static List<Property> getProperties(Map<String, Object> propertiesMap) {
List<Property> propertiesList = new ArrayList<>();
// iterate on properties
for (Map.Entry<String, Object> propertyEntry : propertiesMap.entrySet()) {
Property property = new Property();
property.name = propertyEntry.getKey();
Map<String, Object> propertyAttrsMap = (Map<String, Object>)propertyEntry.getValue();
property.type = (String)propertyAttrsMap.get("type");
// recursively get sub properties
if (propertyAttrsMap.containsKey("properties")) {
property.subProperties = getProperties((Map<String, Object>)propertyAttrsMap.get("properties"));
}
propertiesList.add(property);
}
return propertiesList;
}
public static class Property {
public String name;
public String type;
public List<Property> subProperties = new ArrayList<>();
#Override
public String toString() {
return name + " " + type + " " + subProperties;
}
}
}
using this sample input
{
"description": "Something",
"id": "abc.def.xyzjson#",
"type": "object",
"properties": {
"triggerTime": {
"type": "string",
"description": "Time of adjustment event",
"source": "ab.cd",
"pattern": "something",
"required": true
},
"customerId": {
"type": "string",
"description": "Something",
"source": "ef.gh",
"required": true
},
"complex": {
"type": "string",
"description": "Something",
"source": "ef.gh",
"required": true,
"properties": {
"sub1": {
"type": "int",
"description": "sub1",
"source": "ab.cd",
"required": true
},
"sub2": {
"type": "short",
"description": "sub2",
"source": "ab.cd",
"required": true
}
}
}
}
}
produces the following output:
[triggerTime string [], customerId string [], complex string [sub1 int [], sub2 short []]]

Categories