Can somebody help me, how I can deserialize the following JSON, which I can not change?
I am using Jackson for serialization.
{
"columns": [
{
"header": "Heading1",
},
{
"header": "Heading2",
}
],
"rows": [
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
]
}
Columns can have unknown number of headers and their value eg. "Header1" is used in the rows array.
So far I have the following structure:
public class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
public class ColumnConfig {
private String header;
}
public class QueryResultRow {
private int id;
private Map<String, CellValue> values;
}
public class CellValue{
private String value;
}
The problem is that the Map is empty when I deserialize into QueryResult;
I read about TypeReference but I do not know how I can specify a TypeReference<HashMap<String,CellValue>> for the property values in QueryResultRow.
Edit:
My ObjectMapper code is the following:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String queryResultString = loadQuery(queryPath);
QueryResult result = mapper.readValue(queryResultString, QueryResult.class);
The content of queryResultString is the JSON above.
First problem is your JSON is invalid. I assume it should be something like this,
{
"columns": [
{
"header": "Heading1"
},
{
"header": "Heading2"
}
],
"rows": [
{
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
}
]
}
Then answer is quite straightforward. You need to change your QueryResultRow as follows,
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
Then I think you should good to go.
Here is a complete working example,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"columns\":[{\"header\":\"Heading1\"},{\"header\":\"Heading2\"}],\"rows\":[{\"id\":1,\"Heading1\":{\"value\":\"Value1\"},\"Heading2\":{\"value\":\"Value2\"}}]}";
ObjectMapper om = new ObjectMapper();
QueryResult queryResult = om.readValue(s, QueryResult.class);
System.out.println(queryResult);
}
}
#Getter
#Setter
#ToString
class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
#Getter
#Setter
#ToString
class ColumnConfig {
private String header;
}
#Getter
#Setter
#ToString
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
#Getter
#Setter
#ToString
class CellValue{
private String value;
}
Related
I have to use the exchange() method because I pass HttpHeaders there.
ResponseEntity<WeatherResponse> response Entity = restTemplate.exchange(
weather UrlRequest, Http Method.GET, new HttpEntity<>(headers), WeatherResponse.class);
JSON:
{
"geoloc": {
"city": {
"id": 213,
"name": "Boston"
},
"country": {
"id": 213,
"name": "USA"
},
"temp": {
"value": 19.4
}
}
Object to deserialization:
class WeahterResponse{
String country;
String city;
float temp;
}
How to influence deserialization in this case. There are two objects in JSON, and I need one?
class WeahterResponse{
GeoLocation geoloc;
Map<String,String> temp;
}
class GeoLocation {
Map<String,Map<String,Object> geoData;
}
It will deserialize your data to WeatherResponse.
Now if you want to get city data or country Data you can get that as follows.
suppose json is deserialized into weatherResponse.
Map<String,Map<String,Object> geoData = weatherResponse.getGeoLoc();
if(!CollectionUtils.isEmpty(geoData)){
if(geoData.containsKey("city")){
Map<String,Object> cityData = geoData.get("city");
System.out.println(cityData.get("id");
System.out.println(cityData.get("name");
}
//same for other keys of geoLoc
//to get Temp value
Map<String,String> temp = weatherResponse.getTemp();
System.out.println(temp.get("value");
Thanks for João Dias.
I made custom deserializer. https://www.baeldung.com/jackson-deserialization
In my case:
public class WeatherDeserializer extends JsonDeserializer<WeatherResponse> {
#Override
public WeatherResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode nodeTree = jsonParser.getCodec().readTree(jsonParser);
JsonNode geoObjectNode = nodeTree.get("geo_object");
JsonNode factNode = nodeTree.get("fact");
String country = geoObjectNode.get("country").get("name").textValue();
String province = geoObjectNode.get("province").get("name").textValue();
String locality = geoObjectNode.get("locality").get("name").textValue();
GeoObject geoObject = new GeoObject(country, province, locality);
Short temp = factNode.get("temp").shortValue();
Long obsTime = factNode.get("uptime").longValue();
return new WeatherResponse(geoObject,temp,obsTime);
}
}
#AllArgsConstructor
#Getter
#JsonDeserialize(using = WeatherDeserializer.class)
public class WeatherResponse {
private GeoObject geoObject;
private Short temp;
private Long uptime;
}
I was trying convert to my object to and from json but the default serializer, deserializer by jackson doesn't work.
How can I make this work? I understand I might need to write a custom serializer, deserializer. How can I do that?
Is ther some annotation by adding which the code would work?
Here is the object:
#JsonDeserialize(keyUsing = mypairDeserializer.class)
#JsonSerialize(keyUsing = mypairSerializer.class)
HashMap<Set < Mypair > , List < Mypair > > obj;
public class ConditionSerializer extends JsonSerializer<Collection<mypair>> {
#Override
public void serialize(final Collection<mypair> conditionSet, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("Pair");
jsonGenerator.writeStartArray();
for(final Condition condition: conditionSet) {
jsonGenerator.writeString(mypair.toString());
}
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
}
}
public class mypairDeserializer extends KeyDeserializer {
ObjectMapper mapper = new ObjectMapper();
#Override
public Collection<mypair> deserializeKey(final String key, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
// return new mypair(key);
return mapper.readValue(key, Collection.class);
}
}
Hi again from last post,
So, this is an example of what you can do :
Note that since I don't know what is your object Mypair, I did this example with a User class :
public class User {
private int id;
private String name;
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
// getters & setters
}
The class containing your complex object :
public class YourClass {
#JsonSerialize(using = ComplexObjectSerializer.class)
private Map<Set<User>, List<User>> object;
public YourClass(Map<Set<User>, List<User>> object) {
this.object = object;
}
public Map<Set<User>, List<User>> getObject() {
return object;
}
public void setObject(Map<Set<User>, List<User>> object) {
this.object = object;
}
}
The custom serializer :
public class ComplexObjectSerializer extends StdSerializer<Map<Set<User>, List<User>>> {
public ComplexObjectSerializer() {
this(null);
}
public ComplexObjectSerializer(Class<Map<Set<User>, List<User>>> t) {
super(t);
}
private static final long serialVersionUID = 1L;
#Override
public void serialize(Map<Set<User>, List<User>> complexObject,
JsonGenerator jsonGen, SerializerProvider arg2) throws IOException {
// Suppose you want the following json:
/**
* [ { "set":[], "list":[] } ]
*/
jsonGen.writeStartArray(); // [
for (Entry<Set<User>, List<User>> entry : complexObject.entrySet()) {
jsonGen.writeStartObject(); // {
jsonGen.writeObjectField("set", entry.getKey()); // It will call the default serializer for a Set<User>, ie : [ {"id": 0, "name":"string"} ]
jsonGen.writeObjectField("list", entry.getValue()); // It will call the default serializer for a List<User>, ie the same thing as the Set above
jsonGen.writeEndObject(); // }
}
jsonGen.writeEndArray(); // ]
}
}
Main :
Map<Set<User>, List<User>> complexObject = new HashMap<Set<User>, List<User>>();
// Add some data in the map ...
YourClass yourClass = new YourClass(complexObject);
// Serialize your object
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(yourClass); // It will call your custom serializer
System.out.println(json);
Output :
{
"object": [
{
"set": [
{
"id": 5,
"name": "userName5"
},
{
"id": 6,
"name": "userName6"
}
],
"list": [
{
"id": 2,
"name": "userName2"
}
]
},
{
"set": [
{
"id": 4,
"name": "userName4"
},
{
"id": 3,
"name": "userName3"
}
],
"list": [
{
"id": 0,
"name": "userName0"
},
{
"id": 1,
"name": "userName1"
}
]
}
]
}
Here is the sample JSON string. I want to parse this nested JSON object even though nested object have the same name. Some time we may have multiple levels of the nested objects. I tired with Jackson nested objects parsing but that did not work for me. After parsing the object, i want to convert that into a different format.Please help me in parsing this JSON. Thanks in advance.
{
"operator": "and",
"predicates": [
{
"operator": "and",
"predicates": [
{
"columnName": "userName",
"datatype": "string",
"input": "text",
"operand": "equal",
"value": "xxxx"
},
{
"columnName": "Age",
"datatype": "number",
"input": "number",
"operand": "greater_or_equal",
"value": "21"
}
]
},
{
"operator": "and",
"predicates": [
{
"columnName": "userName",
"datatype": "string",
"input": "text",
"operand": "not_equal",
"value": "nnn"
},
{
"columnName": "Birthday",
"datatype": "date",
"input": "date_picker",
"operand": "in",
"value": "2020-07-23,2020-07-24"
}
]
}
]
}
below is the code in java
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Predicates {
private String columnName;
private String datatype;
private String input;
private String operator;
private String value;
private String operand;
/**
*
*/
private List<Predicates> predicates;
#JsonProperty("predicates")
private void unpackNested(Map<String,Object> predicates) {
this.columnName = (String)predicates.get("columnName");
this.datatype = (String)predicates.get("datatype");
this.input = (String)predicates.get("input");
this.operator = (String)predicates.get("operator");
this.value = (String)predicates.get("value");
this.operand = (String)predicates.get("operand");
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDatatype() {
return datatype;
}
public void setDatatype(String datatype) {
this.datatype = datatype;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getOperand() {
return operand;
}
public void setOperand(String operand) {
this.operand = operand;
}
public List<Predicates> getPredicates() {
return predicates;
}
public void setPredicates(List<Predicates> predicates) {
this.predicates = predicates;
}
}
Parsing
ObjectMapper mapper = new ObjectMapper();
Predicates pr = mapper.readValue(json, Predicates.class);
I don't know what you are trying to achieve with your unpackNested method. Jackson can already bind properties from your JSON to your objects and handles recursive properties just fine.
I simply removed your unpackNested method and ran your code on your provided input:
ObjectMapper mapper = new ObjectMapper();
Predicates pr = mapper.readValue(json, Predicates.class);
The object pr contains the full json including the nested child predicates. (I ran the code with jackson databind 2.11.2).
If your properties are not auto-detected, annotate your getters with #JsonProperty:
class Predicate {
// ..snip..
#JsonProperty("input")
public String getInput() {
return input;
}
#JsonProperty("predicates")
public List<Predicates> getPredicates() {
return predicates;
}
}
But apart from that, no extra steps need to be taken. Jackson can already unpack nested objects, just remove your (weird) unpackNested method and let Jackson do its job.
I am trying to map my incoming json payload to an arraylist of my model class.
I have a solution but its unintuitive.
I try to do this but get compilation errors-
ObjectMapper mapper = new ObjectMapper();
ArrayList<ModelClass> = mapper.readValue(items, RoleAttribute.class);
FYI I am trying to save this data in a Mongo collection.
Controller-
#PostMapping(value="/resource", consumes="application/json")
public Iterable<ModeClass> createResources(#RequestBody JSONObject requestBody ) throws JsonParseException, JsonMappingException, IOException {
System.out.println(requestBody.getClass());
return serviceImpl.saveResources(requestBody);
}
Model class-
#Data
#AllArgsConstructor
#Document(collection="collection-name")
public
class ModelClass{
#Field
private String ID;
#Field
private String description;
}
The payload is coming in the following format-
{
"data": [
{
"ID": "1",
"description": "desc1"
},
{
"ID": "2",
"description": "desc2"
},
{
"ID": "3",
"description": "desc3"
},
{
"ID": "4",
"description": "desc4"
}
....
]
}
I know I should be using jackson but I can't seem to figure this out. Do I need to change my POJO? Do I need to create custom Jackson config?
You can do it with json annotation. I also notice that your values are represented as data in json so that also needs to be taken care of. Look at below code. That will solve your problem.
import com.fasterxml.jackson.annotation.JsonProperty;
#Data
#AllArgsConstructor
#Document(collection="collection-name")
public class ModelClass{
#Field
#JsonProperty("ID")
private String classID;
#Field
#JsonProperty("description")
private String classDescription;
public String getClassID() {
return classID;
}
public void setClassID(String classID) {
this.classID = classID;
}
public String getClassDescription() {
return classDescription;
}
public void setClassDescription(String classDescription) {
this.classDescription = classDescription;
}
}
And wrapper Data class as below
class Data {
ModelClass[] data;
public ModelClass[] getData() {
return data;
}
public void setData(ModelClass[] data) {
this.data = data;
}
}
And json conversion code as below
ObjectMapper mapper = new ObjectMapper();
// json is your incoming json as a string. You can put inputstream also
Data values = mapper.readValue(json, Data.class);
System.out.println(values.getData().length);
System.out.println(values.getData()[0].getClassID());
You would need a container class for the data field, something like:
#Data
#Document(collection="collection-name")
public class DataClass{
private List<ModelClass> data;
}
Doing it via Jackson should be automatic this way, in controller:
public Iterable<ModeClass> createResources(#RequestBody DataClass requestBody ) {
I have a JSON string like:
"shipping_profiles": {
"563": {
"name": "name",
"value": "value"
},
"564": {
"name": "name",
"value": "value"
},
"565": {
"name": "name",
"value": "value"
},
"566": {
"name": "name",
"value": "value"
}
}
Now I am parsing it with Jackson 2.0.
I am trying to get a List<shipping_profiles> from the JSON string.
Is it possible?
Your shipping_profiles property doesn't look like array. It represent object with dynamic properties, so we should treat it like an object. If we do not know anything about properties we can use #JsonAnySetter annotation. Algorithm could looks like below:
Deserialize JSON into JSON-model classes.
Convert dynamic objects (maps) into app's POJO classes using ObjectMapper
Use app's POJO whenever you want.
Please see my example implementation. I hope, it help you solve your problem. Input JSON:
{
"shipping_profiles":{
"563":{
"name":"name563",
"value":"value563"
},
"564":{
"name":"name564",
"value":"value564"
},
"565":{
"name":"name565",
"value":"value565"
},
"566":{
"name":"name566",
"value":"value566"
}
}
}
Example program:
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
File source = new File("X:/test.json");
Entity entity = mapper.readValue(source, Entity.class);
ShippingProfiles shippingProfiles = entity.getShippingProfiles();
List<Map<String, String>> profileMaps = shippingProfiles.getProfiles();
List<Profile> profiles = new ArrayList<Profile>(profileMaps.size());
for (Map<String, String> item : profileMaps) {
profiles.add(mapper.convertValue(item, Profile.class));
}
System.out.println(profiles);
}
}
class Entity {
#JsonProperty("shipping_profiles")
private ShippingProfiles shippingProfiles;
public ShippingProfiles getShippingProfiles() {
return shippingProfiles;
}
public void setShippingProfiles(ShippingProfiles shippingProfiles) {
this.shippingProfiles = shippingProfiles;
}
}
class ShippingProfiles {
private List<Map<String, String>> profiles = new ArrayList<Map<String, String>>();
#JsonAnySetter
public void setDynamicProperty(String name, Map<String, String> map) {
profiles.add(map);
}
public List<Map<String, String>> getProfiles() {
return profiles;
}
public void setProfiles(List<Map<String, String>> profiles) {
this.profiles = profiles;
}
}
class Profile {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return "Profile [name=" + name + ", value=" + value + "]";
}
}
Above app prints:
[Profile [name=name563, value=value563], Profile [name=name564, value=value564], Profile [name=name565, value=value565], Profile [name=name566, value=value566]]
I got my json with dynamic property parsed with the way #michalziober provide.
"commandClasses": {
"32": {
"name": "Basic",
"data": {
"name": "devices.1.instances.1.commandClasses.32.data",
"value": null,
"type": "NoneType"
},
"38": {
"name": "SwitchMultilevel",
"data": {
"name": "devices.1.instances.1.commandClasses.38.data",
"value": null,
"type": "NoneType"
},
"43": {
"name": "SceneActivation",
"data": {
"name": "devices.1.instances.1.commandClasses.43.data",
"value": null,
"type": "NoneType"
}
With this json I also need to save that dynamic property, so I add another List for storing it.
public class CommandClasses {
private List<String> nameList = new ArrayList<String>();
private List<CommandClass> commmandClasses = new ArrayList<CommandClass>();
private Logger logger = Logger.getInstance(CommandClasses.class);
#JsonAnySetter
public void setDynamicCommandClass(String name, CommandClass cc) {
logger.d("# adding new CC : " + name);
nameList.add(name);
commmandClasses.add(cc);
}
public List<CommandClass> getCommmandClasses() {
return commmandClasses;
}
public void setCommmandClasses(List<CommandClass> commmandClasses) {
this.commmandClasses = commmandClasses;
}
}
Now I can also access the field as id to send out request later.