Spring MVC MappingJackson2JsonView Collection Element Repeated - java

Given Person.java:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String lastName;
private String firstName;
private String middleName;
// ...
public String getLastName() {
return lastName;
}
public String getFirstName() {
return firstName;
}
public String getMiddleName() {
return middleName;
}
}
and Persons.java:
public class Persons implements Serializable {
private static final long serialVersionUID = 1L;
private final List<Person> persons;
public Persons(List<Person> persons) {
this.persons = persons;
}
public List<Person> getPersons() {
return persons;
}
}
When returning a JSON response for Persons, the "persons" element is repeated:
{
"persons" : {
"persons" : [ {
"lastName" : "McCartney",
"firstName" : "James",
"middleName" : "Paul"
}, {
"lastName" : "Lennon",
"firstName" : "John",
"middleName" : "Winston"
}, {
"lastName" : "Starkey",
"firstName" : "Richard",
"middleName" : null
}, {
"lastName" : "Harrison",
"firstName" : "George",
"middleName" : null
} ]
}
}
How do I remove the extra element?

The reason is probably because you have an model attribute named persons which you are returning for MappingJackson2JsonView to convert to json:
model.addAttribute("persons", personsType);
There are two good fixes that I can think of:
To use #ResponseBody annotated controller methods instead, this way you can return persons and MappingJackson2HttpMessageConverter would convert your type cleanly to json
If you want to continue with your approach, you can customize MappingJackson2JsonViewwith an additional flag to indicate that it has to extract value from the model before serializing to json - see here

Related

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

Send nested object to Spring POST

I have this JSON String send by Angular:
{
"transaction_id": "1234",
"usage": "Test Usage",
"billing_address": {
"first_name": "name",
"last_name": "name",
"address1": "street 1234",
"zip_code": "11923"
},
"shipping_address": {
"first_name": "name",
"last_name": "name",
"address1": "street 1234",
"zip_code": "11923"
}
}
Java code:
public class DTO {
private String transaction_id;
private String usage;
private BillingAddress billingAddress;
private ShippingAddress shippingAddress;
... getter/setter
}
public class BillingAddress {
private String firstName;
private String lastName;
private String address1;
private String zip_code;
... getter/setter
}
public class ShippingAddress {
private String firstName;
private String lastName;
private String address1;
private String zip_code;
... getter/setter
}
Spring endpoint:
#PostMapping(value = "/{id}", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> handleWpfMessage(#PathVariable("id") id,
#RequestBody DTO data){
....
}
What is the proper way to map the inner objects for billing_address and shipping_address in order values to be mapped properly? Do I need to add annotations in order to map them properly?
You should add the following annotations to your DTO class:
public class DTO {
private String transaction_id;
private String usage;
#JsonProperty("billing_address")
private BillingAddress billingAddress;
#JsonProperty("shipping_address")
private ShippingAddress shippingAddress;
... getter/setter
}
Your angular client uses snake case. In order to make jackson deserializing properly you can configure it globally with :
spring.jackson.property-naming-strategy=SNAKE_CASE
However you can also configure it for a specific class :
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class DTO {
}
As already mentioned, you can use the Jackson property mapping annotaion in your DTO class.
#JsonProperty("billing_address")
private BillingAddress billingAddress;
This means, in the json, attribute billing_address will be assigned to billingAddress variable.

How to map A JSON single item from a list<int, String) to String with Jackson?

In some incoming JSON there is a list
"age" : 27,
"country", USA,
"fields": [
{
"id": 261762251,
"value": "Fred"
},
{
"id": 261516162,
"value": "Dave"
},
]
I know the key int for what I am looking for [261762251].
I would like to map that to a plain String field firstname in the User object with the rest of the bottom level fields from the JSON. I have tried extending com.fasterxml.jackson.databind.util.StdConverter and adding the annotation #JsonSerialize(converter=MyConverterClass.class) to the variable in the User class with no luck.
My architecture is like this:
public class User {
private String age;
private String country;
private String firstname; // this is the field in the list that needs converting
// getters and setters
}
public class ApiClient{
public User getUsers(){
Response response;
//some code to call a service
return response.readEntity(User.class)
}
}
What is the best approach to achieve this?
You can try something like below:
class Tester
{
public static void main(String[] args) throws Exception {
String s1 = "{\"fields\": [ { \"id\": 261762251, \"value\": \"Fred\" }, { \"id\": 261516162, \"value\": \"Dave\" }]}";
ObjectMapper om = new ObjectMapper();
Myclass mine = om.readValue(s1, Myclass.class);
System.out.println(mine);
}
}
public class User {
private String age;
private String country;
private String firstname; // this is the field in the list that needs converting
#JsonProperty("fields")
private void unpackNested(List<Map<String,Object>> fields) {
for(Map<String,Object> el: fields) {
if((Integer)el.get("id") == 261762251) {
firstname = el.toString();
}
}
}
// getters and setters
}

How to use Jackson Annotation to do a mapping

Simply I have a POJO like this:
#JsonInclude(value=Include.NON_EMPTY)
public class Contact {
#JsonProperty("email")
private String email;
#JsonProperty("firstName")
private String firstname;
#JsonIgnore
private String subscriptions[];
...
}
When I create the JSON object using the JsonFactory and ObjectMapper, it would be something like:
{"email":"test#test.com","firstName":"testName"}
Now, the question is how can I generate something like the following without manual mapping.
{"properties": [
{"property": "email", "value": "test#test.com"},
{"property": "firstName", "value": "testName"}
]}
Note that, I know how to do manual mapping. Also, I need to use some features like Include.NON_EMPTY.
You can implement two steps processing as follows.
Firstly, you convert your bean instance to a JsonNode instance using ObjectMapper. This guaranties applying all the Jackson annotations and customization. Secondly, you manually map the JsonNode fields to your "property-object" model.
Here is an example:
public class JacksonSerializer {
public static class Contact {
final public String email;
final public String firstname;
#JsonIgnore
public String ignoreMe = "abc";
public Contact(String email, String firstname) {
this.email = email;
this.firstname = firstname;
}
}
public static class Property {
final public String property;
final public Object value;
public Property(String property, Object value) {
this.property = property;
this.value = value;
}
}
public static class Container {
final public List<Property> properties;
public Container(List<Property> properties) {
this.properties = properties;
}
}
public static void main(String[] args) throws JsonProcessingException {
Contact contact = new Contact("abc#gmail.com", "John");
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.convertValue(contact, JsonNode.class);
Iterator<String> fieldNames = node.fieldNames();
List<Property> list = new ArrayList<>();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
list.add(new Property(fieldName, node.get(fieldName)));
}
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(new Container(list)));
}
}
Output:
{ "properties" : [ {
"property" : "email",
"value" : "abc#gmail.com"
}, {
"property" : "firstname",
"value" : "John"
} ] }
With a little effort you can re-factor the example to a custom serializer which can be plugged as documented here.

Order of JSON objects using Jackson's ObjectMapper

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

Categories