I want to create this JSON using jakson annotated POJOS. The issue I have when I create a new class without #JsonProperty annotation to represent the last {"id":"123ccc","role":"dddd"}, it by default take the class name and create something like "customer":{"id": "123ccc","role":"dddd"}.
The JSON Structure I indent to build
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
{
"id": "123ccc",
"role": "dddd"
}
]
}
The JSON I'm receiving from the below code.
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
"customer" : {
"id": "123ccc",
"role": "dddd"
}
]
}
What would be a workaround to get the exact JSON format as the image. Current Implementation is below.
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class RelatedParty {
#JsonProperty(value = "contact")
private List<Contact> contact;
#JsonProperty(value = "role")
private String role;
#JsonProperty(value = "fullName")
private String fullName;
private Customer customer;
public List<Contact> getContact() {
return contact;
}
public void setContact(List<Contact> contact) {
this.contact = contact;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
public class Customer {
#JsonProperty(value = "id")
private String id;
#JsonProperty(value = "role")
private String role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
You need to create additional and different POJO classes to model your JSON correctly. Basically, JSON arrays will be handle in Java lists, and JSON objects will be handled in Java classes.
Starting from the inside (most nested level) of the JSON, and working our way out:
NOTE: getters and setters not shown here
Characteristic.java
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Characteristic {
#JsonProperty("city")
private String city;
#JsonProperty("country")
private String country;
#JsonProperty("emailAddress")
private String emailAddress;
}
Contact.java (contains our characteristics):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Contact {
#JsonProperty("mediumType")
private String mediumType;
#JsonProperty("characteristic")
private Characteristic characteristic;
}
The above two classes handle the innermost objects. If we remove them from your target JSON, that leaves the following:
{
"relatedParty": [{
"contact": [...],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}
Note that the contact field is a JSON array, not an object - so we do not create a Java Contact class (which would be for a JSON object).
To handle the above I create two more classes:
RelatedPartyInner.java (contains a list of contacts)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty_ {
#JsonProperty("contact")
private List<Contact> contact = null;
#JsonProperty("role")
private String role;
#JsonProperty("fullName")
private String fullName;
#JsonProperty("id")
private String id;
}
RelatedParty.java (wraps everything in an outer object):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty {
#JsonProperty("relatedParty")
private List<RelatedPartyInner> relatedParty = null;
}
To test this I create the following data:
Characteristic chr1 = new Characteristic();
chr1.setCity("xxx");
chr1.setCountry("xxx");
Characteristic chr2 = new Characteristic();
chr2.setEmailAddress("yyy#yy.yyy");
Contact con1 = new Contact();
con1.setMediumType("xxx");
con1.setCharacteristic(chr1);
Contact con2 = new Contact();
con2.setMediumType("yyy");
con2.setCharacteristic(chr2);
List<Contact> cons = new ArrayList<>();
cons.add(con1);
cons.add(con2);
RelatedPartyInner rpi1 = new RelatedPartyInner();
rpi1.setContact(cons);
rpi1.setRole("ccc");
rpi1.setFullName("ccc");
RelatedPartyInner rpi2 = new RelatedPartyInner();
rpi2.setId("123ccc");
rpi2.setRole("dddd");
List<RelatedPartyInner> rpis = new ArrayList<>();
rpis.add(rpi1);
rpis.add(rpi2);
RelatedParty rp = new RelatedParty();
rp.setRelatedParty(rpis);
Finally, we can generate the JSON:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(new File("rp.json"), rp);
The resulting file contains the following:
{
"relatedParty": [{
"contact": [{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
}, {
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}
Related
I'm learning how to properly use DTOs and so far everything was going well until I got this problem:
I have a Model called Student:
#Data
#Document
#Builder
public class Student {
#Id
private String id;
#Indexed(unique = true)
private Name name;
#Indexed(unique = true)
private String email;
private Gender gender;
private Address address;
private List<String> favoriteSubjects;
private BigDecimal totalSpentInBooks;
private LocalDateTime created;
}
Than I create a DTO to fetch some data:
#Data
public class StudentDto {
private String firstName;
private String lastName;
private String email;
private String country;
}
Here is the response using the DTO from Postman:
{
"firstName": "Gabriel",
"lastName": "Vendramini",
"email": "gabriel.vendramini#email.com",
"country": null
}
Here is the same response but with no DTO:
{
"id": "61f7d87bb4534b452993b02e",
"name": {
"firstName": "Gabriel",
"lastName": "Vendramini"
},
"email": "gabriel.vendramini#email.com",
"gender": "MALE",
"address": {
"country": "Brazil",
"city": "Sao Paulo",
"postCode": "08006"
},
"favoriteSubjects": [
"Computer Science",
"Mathematic",
"Physic"
],
"totalSpentInBooks": 10,
"created": "2022-01-31T13:39:23.023"
}
Here are the Controller:
#GetMapping("/get-all")
public ResponseEntity<List<Student>> fetchAllStudents() {
log.info("Get All Students - Controller Call");
return ResponseEntity.ok(studentService.getAllStudents());
}
#GetMapping("/get-all-dto")
public ResponseEntity<List<StudentDto>> fetchAllStudentsDto() {
log.info("Get All Students With DTO - Controller Call");
return ResponseEntity.ok(studentService.getAllStudents().stream().map(studen
t -> modelMapper.map(student,
StudentDto.class)).collect(Collectors.toList()));
}
Here are the Models for Address and Name:
#Data
#AllArgsConstructor
#Builder
public class Address {
private String country;
private String city;
private String postCode;
}
#Data
#AllArgsConstructor
#Builder
public class Name {
private String firstName;
private String lastName;
}
The structure is the same for both. I tried to pass all the attributes from Address, change the order.
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.
How can I use MapStruct to create a mapper that maps a list (my source) to a object with a list (destination)?
My source classes looks like this:
class SourceB {
private String name;
private String lastname;
}
class SourceA {
private Integer id;
private List<SourceB> bs;
}
so I need to transform it to this:
class DestinationA {
private Integer id;
private DestinationAB bs;
}
class DestinationAB {
private List<DestinationB> b;
}
class DestinationB {
private String name;
private String lastname;
}
Expected sample json:
source:
{
"id": 1,
"bs": [
{
"name": "name1",
"lastname": "last1"
},
{
"name": "name2",
"lastname": "last2"
}
]
}
destination:
{
"id": 1,
"bs": {
"b": [
{
"name": "name1",
"lastname": "last1"
},
{
"name": "name2",
"lastname": "last2"
}
]
}
}
It's quite simple. Just put #Mapping annotation with specified source and destination on top of the mapping method.
#Mapper
public interface SourceMapper {
#Mapping(source = "bs", target = "bs.b")
DestinationA sourceAToDestinationA(SourceA sourceA);
}
I am writing an annotation processor and i need to scan all classes with certain annotation to get all fields and create json object with same structure of the class.
For example:
#ClassToJson
public class Person {
private String name;
private String surname;
/*getter and setter*/
}
My output is:
{
"name": "string",
"surname": "string"
}
Now i am wondering how can i handle classes like this one:
public class PhoneNumber {
private String countryCode;
private String phoneNumber;
/*getter and setter*/
}
#ClassToJson
public class Person {
private String name;
private String surname;
private PhoneNumber phoneNumber;
/*getter and setter*/
}
i would like get output like this:
{
"name": "string",
"surname": "string",
"phoneNumber": {
"countryCode": "string",
"phoneNumber": "string"
}
}
I have a class called Customer.
#Entity
#Table(name = "customer")
public class Customer {
#Id
#Column(unique = true)
private String userId;
#Column(unique = true)
private String userName;
private String fullName;
#Column(unique = true)
private String emailAddress;
private String password;
private String country;
#ElementCollection
private Collection<ContactNum> contactNums = new ArrayList<>();
private String district;
private String dateOfBirth;
private String gender;
}
and there is a collection of contact numbers.
#XmlRootElement
#Embeddable
public class ContactNum {
private String landLine;
#Column(unique = true)
private String mobile;
public String getLandLine() {
return landLine;
}
public void setLandLine(String landLine) {
this.landLine = landLine;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
My REST API is getting a POST Request JSON Object which is Customer and Contact number inside it.
{
"userName": "aaaa",
"fullName": "aaaa",
"emailAddress": "aaaa",
"password": "aaaa",
"country": "aaaa",
"contactNums" : {
"landLine": "0000000000",
"mobile": "0000000000"
},
"district": "aaaa",
"dateOfBirth": "813695400000",
"gender": "aaaa"
}
How can I map that request in my JAX-RS client? My method to get request is this. And I also use Hibernate as an ORM tool.
#POST
#Path("registerCustomer")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response registerCustomer(Customer newCustomer) {
}
If you're using Jackson to handle JSON conversion.
You can either use a custom deserializer (via #JsonDeserialize
on class level).
Or write an adapter which converts your ContactNum
to a Collection of ContactNum's.
But if you change your JSON input from
"contactNums" : {
"landLine": "0000000000",
"mobile": "0000000000"
}
to
"contactNums" : [{
"landLine": "0000000000",
"mobile": "0000000000"
}]
(contactNums changed from object to array of objects)
This way the conversion should work out of the box.