How to avoid too many classes in jackson Java? - java

I have JSON serialization and deserialization is done using Jackson in java.
I have so many JSON fields that to serialize and deserialize I have multiple single-member classes, is there any better way to do this?
I don't have any limitations on using Jackson library, that is the library I have used for most of my cases.
public class Data{
public String type;
public int id;
public Attributes attributes;
public Relationships relationships;
}
public class Category{
public Data data;
}
public class Service{
public Data data;
}
public class Priority{
public Data data;
}
public class Status{
public Data data;
}
public class User{
public Data data;
}
public class Relationships{
public Category category;
public Service service;
public Priority priority;
public Status status;
public User user;
}
public class Root{
public Data data;
}
My sample JSON for which I am serializing looks like below.
{
"data": {
"id": 111,
"type": "op type",
"attributes": {
"title": "Some title"
},
"relationships": {
"category": {
"data": {
"type": "category",
"id": 1
}
},
"service": {
"data": {
"type": "service",
"id": 3
}
},
"priority": {
"data": {
"type": "priority",
"id": 1
}
},
"status": {
"data": {
"type": "status",
"id": 3
}
},
"user": {
"data": {
"type": "user",
"id": 3
}
}
}
}
}

Because Category, Service and others have the same fields data, if you create class manually, you can just create one common class DataWrapper. But I also see you said you use jsonschema2pojo rather than create class manually.
public class Data{
public String type;
public int id;
public Attributes attributes;
public Relationships relationships;
}
public class DataWrapper {
public Data data;
}
public class Relationships{
public DataWrapper category;
public DataWrapper service;
public DataWrapper priority;
public DataWrapper status;
public DataWrapper user;
}
public class Root{
public Data data;
}

Related

jackson deserialize wrapped array

I got the following JSON that im trying to deserialize:
{
"items": [
{
"id": 29000012,
"name": "Crystal League I",
"iconUrls": {
"small": "https://api-assets.clashofclans.com/leagues/72/kSfTyNNVSvogX3dMvpFUTt72VW74w6vEsEFuuOV4osQ.png",
"tiny": "https://api-assets.clashofclans.com/leagues/36/kSfTyNNVSvogX3dMvpFUTt72VW74w6vEsEFuuOV4osQ.png",
"medium": "https://api-assets.clashofclans.com/leagues/288/kSfTyNNVSvogX3dMvpFUTt72VW74w6vEsEFuuOV4osQ.png"
}
},
{
"id": 29000015,
"name": "Master League I",
"iconUrls": {
"small": "https://api-assets.clashofclans.com/leagues/72/olUfFb1wscIH8hqECAdWbdB6jPm9R8zzEyHIzyBgRXc.png",
"tiny": "https://api-assets.clashofclans.com/leagues/36/olUfFb1wscIH8hqECAdWbdB6jPm9R8zzEyHIzyBgRXc.png",
"medium": "https://api-assets.clashofclans.com/leagues/288/olUfFb1wscIH8hqECAdWbdB6jPm9R8zzEyHIzyBgRXc.png"
}
}
],
"paging": {
"cursors": {}
}}
Im trying to deserialize it with the following DTO:
#JsonRootName("items")
#JsonIgnoreProperties(value={ "paging" })
public class League {
private Long id;
private String name;
private IconUrls iconUrls;
public League() {
}
}
class IconUrls {
private String small;
private String tiny;
private String medium;
public IconUrls() {
}
}
But im getting the following error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name ('items') does not match expected ('List') for type `java.util.List<gg.stats.wrapper.entities.League>
I have also set: DeserializationFeature.UNWRAP_ROOT_VALUE
This is the call of the method from my Client:
List<League> getLeagueList();
The problem might be the "paging" key.
Any workaround for that?
I actually found a solution by myself:
#JsonIgnoreProperties(value={ "paging" }, allowGetters=true)
public class ResponseWrapper<T> {
private List<T> items;
#JsonProperty("items")
public List<T> getResponseContent() {
return this.items;
}
#JsonProperty("items")
public void setResponseContent(List<T> items) {
this.items = items;
}
}

Jackson-databind mapping JSON skip layer

I got a JSON response like this:
{
"status": "success",
"response": {
"entries": [
{
"id": 1,
"value": "test"
},
{
"id": 2,
"value": "test2"
}
]
}
}
And i want to map it with jackson-databind on an object like this:
public class Response {
#JsonProperty("status")
private String status;
#JsonProperty("response.entries")
private Collection<ResponseEntry> entries;
}
So i'm searching for an way to give #JsonProperty a path so it can skip the layer "response".
Welcome to Stack Overflow. You can define a wrapper class for your Collection<ResponseEntry> collection like below :
public class ResponseWrapper {
#JsonProperty("entries")
private Collection<ResponseEntry> entries;
}
The ResponseEntry class could be defined like below :
public class ResponseEntry {
#JsonProperty("id")
private int id;
#JsonProperty("value")
private String value;
}
Once defined these classes you can rewrite your old Response class like below :
public class Response {
#JsonProperty("status")
private String status;
#JsonProperty("response")
private ResponseWrapper responseWrapper;
}
You can flatten using the #JsonUnwrapped annotation.
You can have your classes like this
public class Response {
private String status;
private Collection<ResponseEntry> entries;
}
public class ResponseEntry {
#JsonUnwrapped
private Entry entry;
}
pubic class Entry{
private Integer id;
private String value;
}

Deserialize with Dependency Injection

I have a nested Json object and I want to deserialize it into the Account object.
Json example:
{
"status": "OK",
"output": {
"accountNum": ".....",
"customerType": ".....",
"homeNumber": ".....",
"homeIndicator": ".....",
"eligible": true,
"startDate": "2017-01-01",
"contactDetails": {
"firstName": ".....",
"lastName": ".....",
"addressStreet": ".....",
},
"indicators": [
"ind1",
"ind2",
],
"employees": [
{
"name": ".....",
"email": ".....",
"model": ".....",
"program": [
{
"name": ".....",
"date": "....."
},
{
"name": ".....",
"date": "....."
}
],
"type": ".....",
"indicators": [
"....."
],
"customer": false
}
],
}
}
Since it’s a nested Json I am using the following method to do that:
ObjectMapper mapper = new ObjectMapper();
Flux<Timed<XXXDto >> mergedMonos = Flux.fromIterable(jsonList).flatMapSequential(Function.identity());
mergedMonos.map(timed -> mapper.valueToTree(timed.get())).collectList().subscribe(System.out::print);
#Component
public class XXXDto {
#Autowired
private Account account;
#JsonProperty("output")
private void unpackOutput(Map<String, Object> output) {
//Account a1 = new Account();
// this.account.setAccountNum is null
output.get("accountNum");
The problem is that I want to store the "accountNum" in the Account object but during the deserialization the inject Account is null.
I can create an instance in unpackOutput method but I would to see if there is another option via injection.
Any advice will be appreciated.
Thank you
I was able to deserialise the example input using these classes and this code.
First of all, here is the formatted input:
{
"status":"OK",
"output":{
"accountNum":".....",
"customerType":".....",
"homeNumber":".....",
"homeIndicator":".....",
"eligible":true,
"startDate":"2017-01-01",
"contactDetails":{
"firstName":".....",
"lastName":".....",
"addressStreet":"....."
},
"indicators":[
"ind1",
"ind2"
],
"employees":[
{
"name":".....",
"email":".....",
"model":".....",
"program":[
{
"name":".....",
"date":"....."
},
{
"name":".....",
"date":"....."
}
],
"type":".....",
"indicators":[
"....."
],
"customer":false
}
]
}
}
These are the classes I used:
public class ContactDetails{
public String firstName;
public String lastName;
public String addressStreet;
}
public class Program{
public String name;
public String date;
}
public class Employee{
public String name;
public String email;
public String model;
public List<Program> program;
public String type;
public List<String> indicators;
public boolean customer;
}
public class Output{
public String accountNum;
public String customerType;
public String homeNumber;
public String homeIndicator;
public boolean eligible;
public String startDate;
public ContactDetails contactDetails;
public List<String> indicators;
public List<Employee> employees;
}
public class Root{
public String status;
public Output output;
}
And this is the code I used to deserialise:
ObjectMapper objectMapper = new ObjectMapper();
Root root = objectMapper.readValue(input2, Root.class);
It was pretty simple so Im wondering if I missed something.

Unable to deserialize JSON response into Java Objects

I know there are lots of queries on this topic but nothing has been helpful for me to resolve below issue
{
"_embedded": {
"customers": [
{
"id": 101,
"name": "John",
"city": "Ohio"
},
{
"id": 102,
"name": "Tom",
"city": "London"
}
]
}
}
for this I have created below Java objects:
#Data
public class Wrapper {
#JsonProperty("_embedded")
private Customers customer;
}
#Data
public class Customers {
#JsonProperty("customer")
private List<Foo> obj;
}
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Foo{
private int id;
private String name;
private String city;
}
Any help will be greatly appreciated.
You have some naming issues in your original question, but disregarding that, you could structure your classes according to the JSON to make it easier for both yourself and Gson.
Something like this would work:
public class JsonWrapper {
public Embedded _embedded;
}
public class Embedded {
public Customers customers;
}
public class Customers extends ArrayList<Foo>{ }
public class Foo{
public int id;
public String name;
public String city;
}
String json = "{\"_embedded\":{\"customers\":[{\"id\":101,\"name\":\"John\",\"city\":\"Ohio\"},{\"id\":102,\"name\":\"Tom\",\"city\":\"London\"}]}}";
JsonWrapper wrapper = new Gson().fromJson(json, JsonWrapper.class);

Spring Data REST - Is it required implement repository for each concrete class?

I have the following two classes defined in my Spring Data REST project.
Person.java
#Data
#Entity
public abstract class Person implements Serializable, Identifiable<Long> {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
}
Employee.java
#Data
#Entity
public class Employee extends Person{
private String employeeNumber;
}
I have implemented the Person repository and I can use it to persist Employee resources as well by registering a SimpleAbstractTypeResolver as follows.
#Configuration
public class RepositoryConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.registerModule(new SimpleModule("MyCustomModule") {
#Override
public void setupModule(SetupContext context) {
context.addAbstractTypeResolver(
new SimpleAbstractTypeResolver().addMapping(Person.class, Employee.class)
);
}
});
}
}
I get the following response when I send a GET to the persons endpoint.
{
"_embedded": {
"employees": [
{
"firstName": "John",
"lastName": "Doe",
"employeeNumber": "X-11",
"_links": {
"self": {
"href": "http://localhost:8080/employee/1"
},
"employee": {
"href": "http://localhost:8080/employee/1{?projection}",
"templated": true
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/persons{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/persons"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
However when I call the link http://localhost:8080/employee/1 I get a 404 Not Found error message. So am I doing something wrong? Do I have to implement a new repository for every concrete class?

Categories

Resources