Parsing json string to java with complex datastructure(jackson) - java

I am trying to convert the below json string to java object, but i am getting empty object.
Under prop2 object, there can be any number of key value pairs(where key is a string and value is a array )
{
"Level1": {
"prop1": "",
"prop2": {
"one": [{
"ip": "1.2.3.4",
"port": "100"
}],
"ten": [{
"ip": "10.20.20.10",
"port": "200"
}]
}
}
}
I have this class structure, however i am getting ipAndPorts map as empty.
#JsonIgnoreProperties(ignoreUnknown = true)
static class Root {
#JsonProperty("Level1")
private Level1 level1;
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class Level1 {
#JsonProperty("prop2")
private Prop2 prop2;
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class Prop2 {
private Map<String, List<IpAndPort>> ipAndPorts = Collections.emptyMap();
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class IpAndPort {
#JsonProperty("port")
private String port;
}
How should my java class look like, to represent "prop2" correctly?

For the record: The problem was solved by using
#JsonIgnoreProperties(ignoreUnknown = true)
static class Level1 {
#JsonProperty("prop2")
private Map<String, List<IpAndPort>> ipAndPorts = Collections.emptyMap();
}
directly without the Prop2 class. Otherwise Jackson would expect a JSON property called ipAndPorts under prop2 JSON object.

I would sudgest that you would first create your Java class the way want it to look like, then use Jackson to serialize it to JSON. you will see what is the structure of resultant JSON and see if and how you will need to modify your class.

Related

Handling different JSON Request Jackson

I am working in a project where I need to send a request to remote service with 2 different formats
Format 1:
{
"templateId": "template1",
"configurationData": {
"inboundHeaders": [
{
"key": "header1",
"value": "value1"
}, {
"key": "header2",
"value": "value2"
}, {
"key": "header3",
"value": "value3"
}
],
"outboundHeaders": [
{
"key": "header4",
"value": "value4"
}, {
"key": "header5",
"value": "value5"
}, {
"key": "header6",
"value": "value6"
}
]
}
}
Format 2
{
"templateId": "template1",
"configurationData": {
"inboundHeaders": "head1",
"outboundHeaders" : "head2,head3"
}
}
Now I have created one class
#JsonPropertyOrder({ "inboundHeaders", "outboundHeaders"})
public class ConfigurationData {
#JsonProperty("inboundHeaders")
private List<Header> inboundHeaders = null;
#JsonIgnore
#JsonProperty("outboundHeaders")
private List<Header> outboundHeaders = null;
#JsonProperty("inboundHeaders")
private String inboundHeader = null;
#JsonProperty("outboundHeaders")
private String outboundHeader = null;
}
Getters and Setters would go here....
But when I am executing this program. Obviously, I am getting following exception like
com.fasterxml.jackson.databind.JsonMappingException: Multiple fields representing property
How to handle these two different version (java.util.List vs java.lang.String) of requests in one Json POJO?
I think you have two options.
Create two classes and two methods to call remote service like:
Lists:
#JsonPropertyOrder({ "inboundHeaders", "outboundHeaders"})
public class ConfigurationDataLists {
#JsonProperty("inboundHeaders")
private List<Header> inboundHeaders = null;
#JsonIgnore
#JsonProperty("outboundHeaders")
private List<Header> outboundHeaders = null;
}
Strings:
#JsonPropertyOrder({ "inboundHeaders", "outboundHeaders"})
public class ConfigurationDataString {
#JsonProperty("inboundHeaders")
private String inboundHeader = null;
#JsonProperty("outboundHeaders")
private String outboundHeader = null;
}
Use Map
I will prefer option 1.
The answer from Francisco PĂ©rez is absolutely correct but you later clarified your question. The possibilities to limit new classes are - well - limited. You either need to create a class representinng each different DTO or make some sort of manual serializing.
One thing you can do is that you create an interface for different types of configuration data DTOs, so just:
interface IConfigurationData {}
then you have this template create or change it so that configurationData is of type tha interface:
#Getter #Setter
public class Template {
private String templateId;
private IConfigurationData configurationData;
}
Then using the DTO classes in above mentioned answer let them implement this interface, like:
public class ConfigurationDataLists implements IConfigurationData {...}
and
public class ConfigurationDataString implements IConfigurationData {...}
Then you will be able to do two different queries like this:
Template template1 = new Template();
template1.setTemplateId("1");
template1.setConfigurationData(new ConfigurationDataLists());
Template template2 = new Template();
template2.setTemplateId("2");
template2.setConfigurationData(new ConfigurationDataString());
You cannot use the same name to different properties like you did. E.g. - inboundHeaders.
You have to change one of the propertyname. In simple words you have to keep the
#JsonProperty
unique.

Dynamic serialization/deserialization

I use Spring MVC and Jackson to drive the API of a application that I work in. I am faced with the following situation, we need serialize the Person class below in two different ways...
#Entity
Order{
String id;
String name;
String address;
List<Items> items;
}
#Entity
Item{
String id;
String description:
}
The two situations reposes on the serialization or not of the content of the "items" field in accord with the service that was called.
For example, the service http://localhost/order, results without the "items" field.
{
"id": "1",
"name" : "Bill",
"address" : "any address",
}
In the other hands, the second way is http://localhost/order/[id_order]/item/[ids_items], results with the field "items" that was give on the parameter.
{
"id": "1",
"name" : "Bil",
"address" : "any",
"items" : [{
"id" : "33",
"description" : "Item 33"
}]
}
#JsonView
You can use #JsonView to filter fields depending on the context of serialization. It is supported by Spring MVC.
First define your views:
public class View {
interface Default { }
interface Detailed extends Default { }
}
Then annotate your fields using the desired view:
#Entity
public class Order {
#JsonView(View.Default.class)
private String id;
#JsonView(View.Default.class)
private String name;
#JsonView(View.Default.class)
private String address;
#JsonView(View.Detailed.class)
private List<Items> items;
// Getters and setters
}
Finally annotate your controller methods to use a view when serializing the response:
#JsonView(View.Default.class)
#RequestMapping(value = "/order", method = RequestMethod.GET)
public ResponseEntity<Order> getOrder() {
...
}
#JsonView(View.Detailed.class)
#RequestMapping(value = "/order-with-items", method = RequestMethod.GET)
public ResponseEntity<SampleResults> getOrderWithItems() {
...
}
In order to make it work, you may need to disable the default view inclusion in your ObjectMapper:
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
With jackson you can modify the result json string on the fly. For example:
// create a new order
Order order = new Order("id", "name", "addr");
ObjectMapper mapper = new ObjectMapper();
// create a json string with the order
JsonNode node = mapper.valueToTree(order);
//the content of the node at this moment is:
//{"id":"id","name":"name","address":"addr"}
// create an ArrayList with the Items
ArrayList<Item> items = new ArrayList<Item>();
items.add(new Item("id1", "desc1"));
items.add(new Item("id2", "desc2"));
// transform the ArrayList to a json string and add it
// the the previous node with the Order
((ObjectNode)node).put("items", mapper.valueToTree(items));
String jsonString = node.toString();
System.out.println(jsonString);
The final output is:
{"id":"id","name":"name","address":"addr","items":[{"id":"id1","description":"desc1"},{"id":"id2","description":"desc2"}]}
For more information visit the official documentation page: https://github.com/FasterXML/jackson-databind/

How to unmarshall json lists using Spring Boot RestTemplate

I have to parse a REST response in json and it has a lot of nested lists with many objects.
The response contains an item called "ObjectList" which has a list and inside, two elements, "ObjectA" and "ObjectB". I don't know how to parse the response to objects using Jackson annotations.
The json looks like this:
"ObjectList": [
{
"ObjectA": {
"property1": false,
"property2": true
},
"ObjectB": {
"property1": 66,
"property2": true
},
{
"ObjectA": {
"property1": false,
"property2": true
},
"ObjectB": {
"property1": 66,
"property2": true
}
}
]
}
My code looks like this
ResponseEntity<Response> response = restTemplate.exchange(URL, HttpMethod.GET, request, Response.class);
Response response = response.getBody();
Response is:
#JsonIgnoreProperties(ignoreUnknown = true)
public class TimesheetListResponse {
#JsonProperty("ObjectA")
private List<ObjectA> objectAList;
#JsonProperty("ObjectB")
private List<ObjectB> objectBList;
That does not work at all, and I'm confused about how to map this.
According to your requirement the model structure may look like below. Within the objectList map in Response object, you need to add HashMap with keys as "ObjectA"/"ObjectB" string and value as instance of ObjectA/ObjectB. I have taken value type of Map as Object, so that any object type A/B can fit in there. Add corresponding #JsonXXX annotations.
public class Response {
private List<Map<String,Object>> objectList;
//Getters & Setters
}
public class ObjectB {
String propB1;
String propB2;
}
public class ObjectA {
String propA;
String propA1;
}
I also would consider the entry in the list as another wrapper object that can either ObjectA or ObjectB. I.e.
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Parent {
#JsonProperty("ObjectList")
private List<ChildWrapper> objectList = new ArrayList<>();
}
#JsonIgnoreProperties(ignoreUnknown = true)
public final class ChildWrapper {
#JsonProperty("ObjectA")
private Child ObjectA;
#JsonProperty("ObjectB")
private Child ObjectB;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public final class Child {
#JsonProperty("property1")
private int property1;
#JsonProperty("property2")
private boolean property2;
}
It seems that the mapping was fine, I only had to initialize the Arraylist. The main issue was that the endpoint was returning empty because of a parameter that I forgot.

convert json into Value pairs

I have Json(entire format of json can vary everytime) string as follows,
{"domain": {
"id": "file",
"value": "Content",
"popup": {
"menuitem1": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
which i have converted to a map,which has the key and value as follows,
Map<String, Object> jsonMap = new HashMap<String, Object>();
ObjectMapper mapper = new ObjectMapper();
jsonMap = mapper.readValue(jsonString,new TypeReference<HashMap<String,Object>>(){});
KEY :domain
VALUE :{id=file, popup={menuitem=[{value=New, onclick=CreateNewDoc()}, {value=Open, onclick=OpenDoc()}, {value=Close, onclick=CloseDoc()}],}, value=Content}
How to access each value by passing its name to the map,like
map.get(id) = file
map.get(popup) = {menuitem=[{value=New, onclick=CreateNewDoc()}, {value=Open, onclick=OpenDoc()}, {value=Close, onclick=CloseDoc()}]}
map.get(value) = Content
If you want to get from the map the inner field you need to use a very uncomfortable syntax like this:
((Map<String, Object>)jsonMap.get("domain")).get("id") // returns "file"
I think that you can read your json in a convenient class that you can create manually.
The class will have all the fields contained in the json and you will have access to them using get methods.
class DomainClass {
public InnerDomain domain;
}
class MenuItem {
public String value;
public String onclick;
}
class PopupClass {
public List<MenuItem> menuitem1;
}
class InnerDomain {
public String id;
public String value;
public PopupClass popup;
}
The class DomainClass is the main class that you need to use to deserialize your json. Using jackson is pretty easy and it looks like the method that you've used in your case:
ObjectMapper mapper = new ObjectMapper();
DomainClass domainObj = mapper.readValue(jsonString, DomainClass.class);
// print the id field
System.out.println(domainObj.domain.id);

How do I deserialize this JSON using Jackson?

I have json that looks like this:
{
"summary":{
"somefield1":"somevalue1",
"Twilio":{
"field1":"value1",
"field2":"value2"
},
"Tropo":{
"field1":"value1",
"field2":"value2"
},
...
}
}
I would like to deserialize it into a java class that looks like this:
public class Summary {
private String someField1;
private List<Vendor> vendors;
}
public class Vendor {
private String name;
private String field1;
private String field2;
}
So the Twilio and Tropo need to become Vendor objects in a list where Vendor.name == "Twilio" or "Tropo".
I'm sure jackson has the tools I need to work with this structure but I've been striking out with web searches.
You can do it with combination of #JsonRootName and #JsonAnySetter annotations. Your Summary class should look like this:
#JsonRootName("summary")
class Summary {
#JsonProperty("somefield1")
private String someField1;
private List<Vendor> vendors = new ArrayList<Vendor>();
#JsonAnySetter
public void setDynamicProperty(String vendorName, Map<String, String> properties) {
Vendor vendor = new Vendor();
vendor.setName(vendorName);
vendor.setField1(properties.get("field1"));
vendor.setField2(properties.get("field2"));
vendors.add(vendor);
}
//getters,setters,toString methods
}
Example usage:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
System.out.println(mapper.readValue(json, Summary.class));
Above source code shows below string for your JSON:
Summary [someField1=somevalue1, vendors=[Vendor [name=Twilio, field1=value1, field2=value2], Vendor [name=Tropo, field1=value1, field2=value2]]]
If you want to use your objects:
public class Summary {
private String someField1;
private List<Vendor> vendors;
}
public class Vendor {
private String name;
private String field1;
private String field2;
}
you have to modify your json. Actually a structure like the one you defined will be converted to something like:
{
"summary": {
"somefield1": "somevalue1",
"vendors": [
{
"name": "Twilio",
"field1": "value1",
"field2": "value2"
},
{
"name": "Tropo",
"field1": "value1",
"field2": "value2"
}
]
}
}
a list is defined between the square brackets [], and in your case it's a list of objects {}.
I would change your json if you can, because the structure you post will be a mess to work with. The one I pointed out, that matches your java objects, is more clear.
The JSON structure you've got would match this java structure where the key of vendors is the vendor name.
public class Summary {
private String someField1;
private Map<String,Vendor> vendors;
}
public class Vendor {
private String field1;
private String field2;
}
The classes you've specified would support this JSON:
{
"somefield1":"somevalue1",
"vendors":[{
"name":"Twilio"
"field1":"value1",
"field2":"value2"
},
{
"name":"Tropo"
"field1":"value1",
"field2":"value2"
},
...]
}
I don't think you can achieve what you want with jackson as the name is outside the "Vendor" object.

Categories

Resources