How do I deserialize this JSON using Jackson? - java

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.

Related

How do I make a Data model to map the json objects in java?

The single json sample object looks like this:
"Events":[{
"sport_event_id": "sr:sport_event:27636100",
"start_date": "2021-06-22T18:00:00+00:00",
"sport_name": "Soccer",
"competition_name": "UEFA Champions League",
"competition_id": "sr:competition:7",
"season_name": "UEFA Champions League 21/22",
"competitors": [
{
"id": "sr:competitor:37863",
"name": "SS Folgore Falciano Calcio",
"country": "San Marino",
"country_code": "SMR",
"abbreviation": "FFC",
"qualifier": "home",
"gender": "male"
},
{
"id": "sr:competitor:277829",
"name": "FC Prishtina",
"country": "Kosovo",
"country_code": "KOS",
"abbreviation": "PRI",
"qualifier": "away",
"gender": "male"
}
],
"venue": {
"id": "sr:venue:8329",
"name": "Elbasan Arena",
"capacity": 12500,
"city_name": "Elbasan",
"country_name": "Albania",
"map_coordinates": "41.115875,20.091992",
"country_code": "ALB"
},
"probability_home_team_winner": 2.5,
"probability_draw": 88.1,
"probability_away_team_winner": 9.4
},
Please help me understand how to do it. I assume I need to use the Gson library and HashMaps.
I watched a couple of videos on Youtube on how to do a map for a json object. But all I could find is for simple objects with two strings. I can't get around on how to do it for a more complex json file like this. Would be very thankful if somebody posted a code example for this file.
There are a few libraries available in java that can be used to convert json to a Map.
The first popular library is Gson, which can be used to parse json and convert it to a Map with the following code:
import com.google.gson.Gson;
import java.util.Map;
public static void convert(){
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
Gson gson = new Gson();
Map<String, String> map = gson.fromJson(json, Map.class);
...
}
You can read more about that approach here.
Another popular library is Jackson:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public static void convert(){
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> map = objectMapper.readValue(json, Map.class);
...
}
You can read more about that approach right here
I would use a site like this to generate Java classes from your sample JSON (see below). Then use Jackson objectMapper to deserialise as follows:
Root root = objectMapper.readValue(myJsonString, Root.class); */
Then you can do stuff like this to get the name of first competitor in the first event (SS Folgore Falciano Calcio):
root.get(0).competitors.get(0).name
Generated class structure. You probably should make the fields private and add getters.
public class Root{
#JsonProperty("Events")
public ArrayList<Event> events;
}
public class Competitor{
public String id;
public String name;
public String country;
public String country_code;
public String abbreviation;
public String qualifier;
public String gender;
}
public class Event{
public String sport_event_id;
public Date start_date;
public String sport_name;
public String competition_name;
public String competition_id;
public String season_name;
public ArrayList<Competitor> competitors;
public Venue venue;
public double probability_home_team_winner;
public double probability_draw;
public double probability_away_team_winner;
}
public class Venue{
public String id;
public String name;
public int capacity;
public String city_name;
public String country_name;
public String map_coordinates;
public String country_code;
}

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.

Is Jackson Serialization of this string possible without custom serializer?

I want to serialize a JSON-String I receive as a POJO, for further usage in my code, but I am struggling to get it working without writing a custom serializer.
I would prefer as solution without writing a custom serializer, but if that is the only possible way I will write one.
Additionally I believe the data I receive is a weird JSON since the list I request is not sent as list using [] but rather as a object using {}.
I receive the following list/object (shortened):
{
"results": {
"ALL": {
"currencyName": "Albanian Lek",
"currencySymbol": "Lek",
"id": "ALL"
},
"XCD": {
"currencyName": "East Caribbean Dollar",
"currencySymbol": "$",
"id": "XCD"
},
"EUR": {
"currencyName": "Euro",
"currencySymbol": "â?¬",
"id": "EUR"
},
"BBD": {
"currencyName": "Barbadian Dollar",
"currencySymbol": "$",
"id": "BBD"
},
"BTN": {
"currencyName": "Bhutanese Ngultrum",
"id": "BTN"
},
"BND": {
"currencyName": "Brunei Dollar",
"currencySymbol": "$",
"id": "BND"
}
}
}
I created my first POJO for the inner object like this:
public class CurrencyDTO implements Serializable {
private String currencyName;
private String currencySymbol;
private String currencyId;
#JsonCreator
public CurrencyDTO( #JsonProperty( "currencyName" ) String currencyName, #JsonProperty( "currencySymbol" ) String currencySymbol,
#JsonProperty( "id" ) String currencyId )
{
this.currencyId = currencyId;
this.currencyName = currencyName;
this.currencySymbol = currencySymbol;
}
}
which itself is fine. Now I wrote another POJO as a wrapper for the data a layer above which looks like this:
public class CurrencyListDTO implements Serializable {
private List<Map<String, CurrencyDTO>> results;
public CurrencyListDTO()
{
}
}
Adding the annotations #JsonAnySetter or using the #JsonCreator didn't help either, so I removed them again and now I am wondering which little trick could enable the correct serialization of the json.
My Exception is the following:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token
at [Source: (String)"{"results":{"ALL":{"currencyName":"Albanian Lek","currencySymbol":"Lek","id":"ALL"},"XCD":{"currencyName":"East Caribbean Dollar","currencySymbol":"$","id":"XCD"},"EUR":{"currencyName":"Euro","currencySymbol":"â?¬","id":"EUR"},"BBD":{"currencyName":"Barbadian Dollar","currencySymbol":"$","id":"BBD"},"BTN":{"currencyName":"Bhutanese Ngultrum","id":"BTN"},"BND":{"currencyName":"Brunei Dollar","currencySymbol":"$","id":"BND"},"XAF":{"currencyName":"Central African CFA Franc","id":"XAF"},"CUP":{"cur"[truncated 10515 chars]; line: 1, column: 12] (through reference chain: com.nico.Banking.api.data.dto.CurrencyListDTO["results"])
You should change your CurrencyListDTO to:
public class CurrencyListDTO {
private Map<String, CurrencyDTO> results;
// getters and setters
}
Because the results field in the response object is another object with the currencyId as key and no array.
You then can create your list of currencies like this:
ObjectMapper mapper = new ObjectMapper();
CurrencyListDTO result = mapper.readValue(json, CurrencyListDTO.class);
List<CurrencyDTO> currencies = new ArrayList<>(result.getResults().values());
Your CurrencyListDTO should look like below. results property is a JSON Object which should be mapped directly to Map. You can convert it to Collection using keySet or values methods.
class CurrencyListDTO implements Serializable {
private Map<String, CurrencyDTO> results;
public Map<String, CurrencyDTO> getResults() {
return results;
}
public void setResults(Map<String, CurrencyDTO> results) {
this.results = results;
}
#Override
public String toString() {
return "CurrencyListDTO{" +
"results=" + results +
'}';
}
}

Parsing json string to java with complex datastructure(jackson)

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.

Form pojo to parse JSON

My json looks like this :
{
"bid": "181.57",
"ask": "181.58",
"volume": {
"item1": "543.21",
"item2": "123.45",
"timestamp": 1487903100000
},
"last": "181.58"
}
I'm trying to use spring restTemplate to read it into a pojo. My current pojo is this :-
import com.fasterxml.jackson.annotation.JsonProperty;
public class DataModel {
private String last;
private Volume volume;
private String ask;
private String bid;
// Getter and setters
}
class Volume
{
private String timestamp;
#JsonProperty
private String item1;
#JsonProperty
private String item2;
// Gettersand setters
}
The problem is that "item1" and "item2" int the json can change to "item5" and "item6" depending on which entity I am querying for. I get null values if my variables are named item1 and item2. How can I keep generic names for the variables item1 and item2 and still be able to read the values correctly in the generic variables? Is there any annotation that will help here?
I believe this is what you are looking for from a Baeldung tutorial:
3.3. #JsonAnySetter
#JsonAnySetter allows you the flexibility of using a Map as standard properties. On de-serialization, the properties from JSON will simply be added to the map.
Let’s see how this works – we’ll use #JsonAnySetter to deserialize the entity ExtendableBean:
public class ExtendableBean {
public String name;
private Map<String, String> properties;
#JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
}
This is the JSON we need to deserialize:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
And here’s how this all ties in together:
#Test
public void whenDeserializingUsingJsonAnySetter_thenCorrect()
throws IOException {
String json
= "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
ExtendableBean bean = new ObjectMapper()
.readerFor(ExtendableBean.class)
.readValue(json);
assertEquals("My bean", bean.name);
assertEquals("val2", bean.getProperties().get("attr2"));
}
In your case, you would simply query the map for the String values you expect for whichever query you are making.

Categories

Resources