Order of JSON objects using Jackson's ObjectMapper - java

I'm using ObjectMapper to do my java-json mapping.
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
ow.writeValue(new File( fileName +".json"), jsonObj);
this is my java class:
public class Relation {
private String id;
private String source;
private String target;
private String label;
private List<RelAttribute> attributes;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public void setAttributes(List<RelAttribute> attributes) {
this.attributes = attributes;
}
public List<RelAttribute> getAttributes() {
return attributes;
}
}
this is what I get:
{
"id" : "-75da69d3-79c8-4000-a3d8-b10350a57a7e",
"attributes" : [ {
"attrName" : "ID",
"attrValue" : ""
}, {
"attrName" : "Description",
"attrValue" : "Primary Actor"
}, {
"attrName" : "Status",
"attrValue" : ""
} ],
"label" : "new Label",
"target" : "-46b238ac-b8b3-4230-b32c-be9707f8b691",
"source" : "-daa34638-061a-45e0-9f2e-35afd6c271e0"
}
So my question now is, how can I get this json output:
{
"id" : "-75da69d3-79c8-4000-a3d8-b10350a57a7e",
"label" : "new Label",
"target" : "-46b238ac-b8b3-4230-b32c-be9707f8b691",
"source" : "-daa34638-061a-45e0-9f2e-35afd6c271e0",
"attributes" : [ {
"attrName" : "ID",
"attrValue" : ""
}, {
"attrName" : "Description",
"attrValue" : "Primary Actor"
}, {
"attrName" : "Status",
"attrValue" : ""
} ]
}
I want it with same order as in my java declaration. Is there a way to specify it ? Maybe with annotations or stuff like that ?

#JsonPropertyOrder({ "id", "label", "target", "source", "attributes" })
public class Relation { ... }

Do you know there is a convenient way to specify alphabetic ordering?
#JsonPropertyOrder(alphabetic = true)
public class Relation { ... }
If you have specific requirements, here how you configure custom ordering:
#JsonPropertyOrder({ "id", "label", "target", "source", "attributes" })
public class Relation { ... }

The ordering of fields within a generated .class is indeterminate, so you can't count on that.
If you want specific ordering per class then you'll need to use the one of the approaches specified in other answers.
If you want everything to default to alphabetical ordering (e.g. for consistency in how the JSON is structured) then you can configure the ObjectMapper like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setConfig(mapper.getSerializationConfig()
.with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
For more consistent JSON consider also adding:
.with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
One advantage of this approach is that you don't have to modify each class being serialized.

I discovered a third way today in case alphabetic is not your desired sorting order. It turns out adding a #JsonProperty annotation on a field places it last when writing. I discovered that when I wanted to specify a property name which did not conform to java naming conventions.
By Adding an index attribute you can define the order. Lowest index is placed first.
#JsonProperty(index=20)
String prop1;
#JsonProperty(index=10)
String prop2;
Would render:
{"prop2": "valueProp2", "prop1": "valueProp1"}

You can use #XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "response", propOrder = { "prop1", "prop2",
"prop3", "prop4", "prop5", "prop6" }).
#JsonPropertyOrder requires a new jar to be added.

As per this documentation, you can configure Jackson2ObjectMapperBuilder globally. This class is available in spring-web dependency.
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
Also, you can use #JsonProperty(index) to determine the order in inherited classes as well.
class animal {
#JsonProperty(index=2)
int p1;
#JsonProperty(index=3)
int p2;
}
class cat extends animal{
#JsonProperty(index=1)
int p3;
}

Related

Deserialize json with polymorphic recursion using Jackson

I am trying to deserialize an object which has polymorphic recursion on a Map value. The first level always deserialize to the correct type, but the recursions are deserialized as LinkedHashMap.
Code:
public class Rule {
private String ruleName;
private List<Variable> variables;
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#Type(value = ListVariable.class, name = "ListVariable"),
#Type(value = MapVariable.class, name = "MapVariable")
})
public abstract class Variable<T> {
private String name;
private T value;
}
public class ListVariable extends Variable<List<Object>> {
}
public class MapVariable extends Variable<Map<String, Object>> {
private String input;
private Object defaultValue;
}
Json with multiple levels:
{
"ruleName" : "xyz",
"variables" : [ {
"type" : "MapVariable",
"name" : "multi_level_map",
"input" : "listing_country",
"defaultValue" : "Unknown",
"value" : {
"US" : {
"type" : "MapVariable",
"name" : "multi_level_map_2_us",
"input" : "threshold",
"defaultValue" : "Unknown",
"value" : {
"Range(0.0,1.0)" : "Low_US",
"Range(1.0,2.0)" : "Medium_US",
"Range(2.0,3.0)" : "High_US"
}
}
}
} ]
}
Deserialization results:
enter image description here
Does anyone know what I am doing wrong and how can I make this recursive deserialization work?

spring boot jackson generate dynamic property name with hierarchical levels

How to apply dynamic property name with counter in it.?
I am building a spring boot rest api that returns the following response object.
Response object is a structure with hierarchical levels. Instead of showing all the "levels" property name as default "levels", i want to dynamically put the level number to it as explained in the below sample json.
public class Root {
private String id;
private List<Level> levels;
}
public class Level {
private String name;
private List<Level> levels;
}
current json output:
{
"id" :"testid",
"levels" : [
{
"name" :"test1"
"levels" : [
{
"name": "test3"
"levels" : []
}
}
Sample expected json:
{
"id" :"testid",
"level1" : [
{
"name" :"test1"
"level2" : [
{
"name": "test3"
"level3" : []
}
}
i solved it as follows, by adding a field that contains current level and appending that to the field name.
public class Root {
private String id;
#JsonIgnore
private List<Level> levels;
#JsonIgnore
private int level;
#JsonAnyGetter
public Map<String, List<Level>> any() {
return Collections.singletonMap("level"+level, levels);
}
}
public class Level {
private String name;
private List<Level> levels;
#JsonIgnore
private int level;
#JsonAnyGetter
public Map<String, List<Level>> any() {
return Collections.singletonMap("level"+level, levels);
}
}

Generic POJO for JSON Object that can take different type of values

I have a json payload (request payload of a rest api) with a defined schema, but there is one property that can take an array of unknown key value pairs. The value for each property can be of different type like number, string, array, range, date, etc. How do i create a POJO for this property and make deserialization work for the same?
I am currently thinking about writing a custom deserializer for my Property class, where i check the type of value and do some custom logic accordingly.
This looks like a typical requirement. I feel that there should be something available in Jackson or Gson that i am missing. I would love to reuse if it already exist. I looked around in SO, but couldnt find a good answer so far. Any suggestions would be appreciated.
{
"id": 1234,
"name": "test name 1",
"properties": [
{
"key_a": 100
},
{
"key_b": [
"string1",
"string2",
"string3"
]
},
{
"key_c": {
"range": {
"min": 100,
"max": 1000
}
}
}
]
}
I am thinking my POJO for property object would look something like this.
class Property {
private String key;
private Value value;
}
It is possible to use inheritance for that. This is the classes for your example with Jackson
public class Sample {
#JsonProperty(value = "id")
Integer id;
#JsonProperty(value = "name")
String name;
#JsonProperty(value = "properties")
List<Property> properties;
}
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(value = KeyA.class, name = "key_a"),
#JsonSubTypes.Type(value = KeyB.class, name = "key_b"),
#JsonSubTypes.Type(value = KeyC.class, name = "key_c")
})
public abstract class Property {
}
public class KeyA extends Property{
Integer value;
public KeyA(Integer value) {
this.value = value;
}
#JsonValue
public Integer getValue() {
return value;
}
}
public class KeyB extends Property {
List<String> valueList;
#JsonCreator
public KeyB( List<String> valueList) {
this.valueList = valueList;
}
#JsonValue
public List<String> getValueList() {
return valueList;
}
}
public class KeyC extends Property {
#JsonProperty(value = "range")
Range value;
}
public class Range {
#JsonProperty(value = "min")
Integer min;
#JsonProperty(value = "max")
Integer max;
}
If I understand correctly you want to change to JSON and back. I wrote a small class for my own SpringBoot project, using ObjectMapper
#Component
public final class JsonUtils {
private final ObjectMapper mapper;
#Autowired
public JsonUtils(ObjectMapper mapper) {
this.mapper = mapper;
}
public String asJsonString(final Object object) {
try {
return mapper.registerModule(new JavaTimeModule())
.writeValueAsString(object);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*
* Customized Objectmapper for reading values compatible with this class' other methods
* #Return the desired object you want from a JSON
* IMPORTANT! -your return object should be a class that has a #NoArgsConstructor-
*/
public Object readValue(final String input, final Class<?> classToRead) {
try {
return mapper
.readValue(input, classToRead);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}`
Perhaps it can be of some use to you.

Generate objects from JSON with Polymorphism

I have this JSON and I want to generate objects with it.
{
"responseId": "response-id",
"session": "projects/project-id/agent/sessions/session-id",
"queryResult": {
"queryText": "End-user expression",
"parameters": {
"orderNumber": "PQL7648194AAAAA",
"lastName": "RIVERO"
},
"action": "order",
"allRequiredParamsPresent": true,
"intent": {
"name": "projects/project-id/agent/intents/intent-id",
"displayName": "[InfoVuelo] Dia-hora de salida - datos de orden"
},
"languageCode": "es"
},
"originalDetectIntentRequest": {}
}
The fields of "parameters" are variable. For example for one situation parameters could be
{
"parameters": {
"email":"example#example.com"
}
}
Also, I could have
{
"parameters": {
"orderNumber": "PQL7648194AAAAA",
"lastName": "RIVERO"
}
}
I made a class Parameters and 2 classes(OrderNumberAndLastNameParams and EmailParams) that extends Parameters
public class EmailParams implements Parameters {
private String email;
}
public class OrderNumberLastNameParams implements Parameters {
private String orderNumber;
private String lastName;
}
My method to create objects with a given JSON file
public static <T> T buildBodyFromJson(String filePath, Class<T> clazz) {
ObjectMapper myMapper = new ObjectMapper();
T jsonMapper = null;
try {
jsonMapper = myMapper.readValue(new ClassPathResource(filePath).getInputStream(), clazz);
} catch (IOException e) {
fail(e.getLocalizedMessage());
}
return jsonMapper;
}
But when I try to build the object, It tries to instantiate Parameter object instead of children objects getting "Unrecognized field ... not marked as ignorable ".
I'm understanding that you don't really need two separated classes, the problem is that you need to generate a requestbody with this object that doesn't have null / empty fields.
Well, there is an annotation that may help you.
Create a single class for paramters with all the 3 properties and annotate with #JsonInclude(JsonInclude.Include.NON_EMPTY)
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Parameters {
private String email;
private String orderNumber;
private String lastName;
}
This annotation makes empty or null fields to not appears on the requestbody that you're creating.

parse JavaBeans sources to json descriptor

I'm looking for a tool or a way to analyze standard JavaBeans source code (with getters and setters) and generate some sort of json descriptors..
maybe with grunt or ant or whatever.
example:
FilterBean.java:
package com.abc.beans;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class FilterBean implements Serializable {
private static final long serialVersionUID = 7490361447912259765L;
private Map<String, List<LabelValueBean>> filterMapList;
private String name;
public Map<String, List<LabelValueBean>> getFilterMapList() {
return this.filterMapList;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
LabelValueBean.java:
package com.abc.beans;
import java.io.Serializable;
import java.util.List;
public class LabelValueBean implements Serializable {
private static final long serialVersionUID = 1237198378921379812L;
private String label;
private Integer id;
private List<String> values;
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public Integer getId() {
return this.idlabel;
}
public List<String> getValues() {
return this.values;
}
public void setValues(List<String> values) {
this.values = values;
}
}
outputs something like:
com.abc.beans.FilterBean.json:
{
"name" : {
"type" : "String",
"setter" : true
},
"filterMapList" : {
"type" : "Map",
"innerType" : "com.abc.beans.LabelValueBean",
"setter" : false
}
}
com.abc.beans.LabelValueBean.json:
{
"label" : {
"type" : "String",
"setter" : true
},
"values" : {
"type" : "Array",
"innerType" : "String",
"setter" : true
},
"id" : {
"type" : "Integer",
"setter" : false
}
}
any idea ?
There's plenty of tools that will take an object-graph and convert them to JSON. Jackson and GSON are two that are commonly used.
But these things only represent data, and may not given you the full picture about the structure. If you want to communicate the structure outside of a Java environment what you may want to do is generate a "JSON Schema". There's another question which discusses how you might do that with a Jackson module.

Categories