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);
}
Related
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.
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"
}]
}
Currently I'm trying to write a site that interacts with a public API using Feign and Spring.
I'm having trouble deciding how to handle the object mapping for deeply nested JSON.
Ex:
[
{
"type": "console",
"category": "Console",
"result_count": 1,
"shown_count": 1,
"result": [
{
"name": "Nintendo Switch",
"id": "nintendo-switch",
"platform": {
"name": "Nintendo",
"category": "nintendo",
"type": "platform"
},
"image": {
"url": "https://encrypted-tbn1.gstatic.com/shopping?q=tbn:ANd9GcRqJYIheMDjTE9WAHjMSW4bjh7OplS7Bep9CdsBBLWMwGdXim7xOG4&usqp=CAc",
"height": 409,
"width": 631
},
"min_price": 205,
"variations": [
{
"items": [
{
"hex_code": "#696969",
"name": "Gray",
"id": "space-gray",
"type": "color"
},
{
"hex_code": "#C0C0C0",
"name": "Silver",
"id": "silver",
"type": "color"
}
],
"name": "Color",
"type": "color"
},
{
"items": [
{
"name": "Nintendo",
"id": "nintendo",
"type": "platform"
}
],
"name": "Platform",
"type": "platform"
}
]
}
]
}
]
As of now, I have a single Java file with a class for each object in the JSON, and I've considered having the Object mapper just put everything into a HashMap. Is there a more elegant way to do this?
public class SearchResults {
private List<SearchResult> products;
private int resultCount;
private String type;
}
class SearchResult {
private String name;
private String slug;
private Image image;
}
class Image {
private String URL;
private String height;
private String width;
}
Based on the json file provided i have designed the classes and also provided the code to parse the json file to java
public class Console{
String type;
String category;
int result_count;
int show_count;
Result [] result;
}
public class Result{
String name;
String id;
Platform platform;
Image image;
int mini_price;
Variation [] variations;
}
public class Platform{
String name;
String category;
String type;
}
public class Image{
String url;
int height;
int width;
}
public class Variation{
String name;
String type;
Item [] items;
}
public class Item{
String hex_code;
String name;
String id;
String type;
}
code to parse:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
Console[] consoles = objectMapper.readValue(ResourceUtils.getFile("path of json file"), Console[].class);
logger.info("Continents -> {}",(Object)continents);
for(Console console:consoles) {
//read the data accordingly
}
I want to remove items from a list with a conditional value that comes from another List. Here are the objects
public class Student{
private String name;
private String age;
private List<Course> listCourses;
//Setters and getters
}
public Class Course{
private String courseName;
private List<CourseDetail> listCoursesDetail;
//Setters and getters
}
public Class CourseDetail{
private String id;
private String grade;
private String professor;
//Setters and getters
}
So as you can see the object Student has a list, inside that list there is another list from the object CourseDetail. What I want to achieve is to filter or remove elements from private List<CourseDetail> listCoursesDetail where ID is not equal to id from this other object.
public class Process{
private String name;
private List<ProcessDetail> listProcessDetail;
//Setters and getters
}
public class ProcessDetail{
private String id;
//Setters and getters
}
Assume the object Process is populated as follows
{
"name": "process1",
"listProcessDetail": [
{
"id": "456"
},
{
"id": "666"
},
{
"id": "555"
}
]
}
Student is populated as follows.
{
"name": "Kevin",
"age": "22",
"listCourses": [
{
"courseName": "Math",
"listCoursesDetail": [
{
"id": "666",
"grade": "88",
"professor": "Xavier"
},
{
"id": "144",
"grade": "90",
"professor": "Marcus"
},
{
"id": "555",
"grade": "100",
"professor": "Joe"
}
]
}
]
}
The expected result will be:
{
"name": "Kevin",
"age": "22",
"listCourses": [
{
"courseName": "456",
"listCoursesDetail": [
{
"id": "666",
"grade": "88",
"professor": "Xavier"
},
{
"id": "555",
"grade": "100",
"professor": "Joe"
}
]
}
]
}
The element from listCoursesDetail with ID 144 was removed since there is no such value in the object Process.
So far I have these:
Set<String> ids = Process.getListProcessDetail().stream().map(ProcessDetail::id).collect(Collectors.toSet());
In these I stored all the ID's on a Set.
Then my attempt to remove the items:
List<Course> courses = Student.getListCourses().stream().filter(c -> c.getListCoursesDetail().stream().anyMatch(d -> ids.contains(d.getId()))).collect(Collectors.toList());
With these lines of code I get the same Student object as nothing happened.
Assuming you want to modify the existing objects, not create a clone of them with shorter lists, you'd do it like this:
student.getListCourses().forEach(c ->
c.getListCoursesDetail().removeIf(d -> ! ids.contains(d.getId())));
I have a Json list using object with children
{
"id":"154",
"name":"peter",
"children": [
{
"id":"122",
"name": "mick",
"children":[]
},
{
"id":"123",
"name": "mick",
"children":[]
}
]
}
Here is the class of my object:
public class person{
private String id;
private String name;
private List<person> children;
//getters and setters
}
When I try to deserialize this object, I have the following error
Can not deserialize instance of person out of START_ARRAY token
What should I do ?
The JSON contains an array of persons.
Your class a List of person.
Either change the JSON like #Naveed Yadav suggested or change the class to
public class Person{
private String id;
private String name;
private Person[] children;
//getters and setters
}
(BTW the class name should be upper case in Java)
Fix syntax errors in your JSON body and you'll be in a good shape:
{
"id":"154",
"name":"peter",
"children": [
{
"id":"122",
"name": "mick",
"children":[], <== Excess comma
} <== Missing comma
{
"id":"123",
"name": "mick",
"children":[], <== Excess comma
}
]
}
Valid one:
{
"id": "154",
"name": "peter",
"children": [{
"id": "122",
"name": "mick",
"children": []
},
{
"id": "123",
"name": "mick",
"children": []
}
]
}
You need to change your POJO declaration like below:-
public class person{
private String id;
private String name;
private List<Children> children;
//getters and setters
private class Children{
private String id;
private String name;
private String[] children;
}
{
"id":"154",
"name":"peter",
"children":
{
"id":"122",
"name": "mick",
"children":[],
}
{
"id":"123",
"name": "mick",
"children":[],
}
}