We are consuming a third party API on server side which returns response as below class.
public class SourceParent {
private String ultimateParentId;
private String name;
private List<SourceChildren> children;
}
public class SourceChildren {
private String ultimateParentId;
private String immediateParentId;
private String childName;
private String level;
private List<SourceChildren> children
}
Children nesting can be up to any level.
We have to map above object to similar type of target (class structure) class i.e.
public class TargetParent {
private String ultimateParentId;
private String name;
private List<TargeChildren> children;
}
public class TargeChildren {
private String ultimateParentId;
private String immediateParentId;
private String childName;
private String level;
private List<TargeChildren> children
}
Mapping has to be done field by field. We can not return source object from our API to our consumers because if any field name or field changes in source object we don't want our consumers to change their code instead we would just change in the mapper.
Somebody please suggest how to do this mapping efficiently from source to target object in java (preferred java 8 and above);
I am trying to map as below, however got stuck as children can be up to any level.
public TargetParent doMapping(SourceParent source) {
TargetParent target = new Targetparent();
target.setUltimateParentId(source.getUltimateParentId);
.......
target.setChildren() // Don't to how to approach here
}
Appreciate you help!
In your doMapping do this
targetParent.setChildren(sourceParent.children.stream().map(sourceChild -> doMappingForChild(sourceChild)).collect(Collectors.toList()));
And add this function below doMapping, this is a recurcive approach
public TargetChild doMappingForChild(SourceChild source){
TargetChild target = new TargetChild();
...other fields
target .setChildren(sourceParent.children.stream().map(sourceChild -> doMappingForChild(sourceChild)).collect(Collectors.toList()));
}
Related
The webservice return the following JSON string:
{"errorCode":0,"error":"","status":"OK","data":{"id":"1234A"}}
So to get a class that receives the response in a function like this that performs a post in Retrofit:
Call<UploadImageData> postData(#Header("Cookie") String sessionId, #Body UploadImageModal image);
I'd need to make a class like this;
public class UploadImageData {
private int errorCode;
private String error;
private String status;
}
But I'm lost in how I would have to declare the part that would take "data":{"id":"1234A"}, so it gets the data from there correctly.
How could I do this?
Since data is a nested object within the surrounding json object, you can include it as another class in your UploadImageData class.
public class UploadImageData {
private int errorCode;
private String error;
private String status;
private MyDataClass data;
}
public class MyDataClass {
private String id;
}
DonĀ“t forget setter methods oder make fields public.
I have a nested POJO structure defined something like this,
public class Employee {
private String id;
private Personal personal;
private Official official;
}
public class Personal {
private String fName;
private String lName;
private String address;
}
public class Official {
private boolean active;
private Salary salary;
}
public class Salary {
private double hourly;
private double monthly;
private double yearly;
}
I get updates from a service with dot annotaion on what value changed, for ex,
id change --> id=100
address change --> personal.address=123 Main Street
hourly salary change --> official.salary.hourly=100
This POJO structure could be 3-4 level deeps. I need to look for this incoming change value and update the corresponding value in POJO. What's the best way of doing it?
If you would like to create Java objects that allows you to edit fields. You can specify your object fields with the public/default/protected access modifiers. This will enable you to get and set fields such as personal.address or official.salary.hours
This approach is typically frowned upon as the object is no longer encapsulated and any calling methods are welcome to manipulate the object. If these fields are not encapsulated with getters and setters, your object is no longer a POJO.
public provides access from any anywhere.
default provides access from any package
protected provides access from package or subclass.
public class Employee {
public String id;
public Personal personal;
public Official official;
}
public class Personal {
public String fName;
public String lName;
public String address;
}
Here's a quick approach using reflection to set fields dynamically. It surely isn't and can't be clean. If I were you, I would use a scripting engine for that (assuming it's safe to do so).
private static void setValueAt(Object target, String path, String value)
throws Exception {
String[] fields = path.split("\\.");
if (fields.length > 1) {
setValueAt(readField(target, fields[0]),
path.substring(path.indexOf('.') + 1), value);
return;
}
Field f = target.getClass()
.getDeclaredField(path);
f.setAccessible(true);
f.set(target, parse(value, f.getType())); // cast or convert value first
}
//Example code for converting strings to primitives
private static Object parse(String value, Class<?> type) {
if (String.class.equals(type)) {
return value;
} else if (double.class.equals(type) || Double.class.equals(type)) {
return Long.parseLong(value);
} else if (boolean.class.equals(type) || Boolean.class.equals(type)) {
return Boolean.valueOf(value);
}
return value;// ?
}
private static Object readField(Object from, String field) throws Exception {
Field f = from.getClass()
.getDeclaredField(field);
f.setAccessible(true);
return f.get(from);
}
Just be aware that there's a lot to improve in this code (exception handling, null checks, etc.), although it seems to achieve what you're looking for (split your input on = to call setValueAt()):
Employee e = new Employee();
e.setOfficial(new Official());
e.setPersonal(new Personal());
e.getOfficial().setSalary(new Salary());
ObjectMapper mapper = new ObjectMapper();
setValueAt(e, "id", "123");
// {"id":"123","personal":{},"official":{"active":false,"salary":{"hourly":0.0,"monthly":0.0,"yearly":0.0}}}
setValueAt(e, "personal.address", "123 Main Street");
// {"id":"123","personal":{"address":"123 Main Street"},"official":{"active":false,"salary":{"hourly":0.0,"monthly":0.0,"yearly":0.0}}}
setValueAt(e, "official.salary.hourly", "100");
// {"id":"123","personal":{"address":"123 Main Street"},"official":{"active":false,"salary":{"hourly":100.0,"monthly":0.0,"yearly":0.0}}}
I have the following hierarchy:
RESPONSE
public class Response implements Serializable {
#SerializedName("message")
#Expose
private List<Message> messages;
public List<Message> getMessages() {
return messages;
}
public void setMessages(List<Message> messages) {
this.messages = messages;
}
}
MESSAGE
public class Message implements Serializable {
#SerializedName("type")
#Expose
#MessageType
private int type;
#SerializedName("position")
#Expose
#MessagePosition
private String position;
public int getType() {
return type;
}
public String getPosition() {
return position;
}
public void setType(#MessageType int type) {
this.type = type;
}
public void setPosition(#MessagePosition String position) {
this.position = position;
}
}
TEXT -> MESSAGE
public class TextMessage extends Message {
#SerializedName("text")
#Expose
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
IMAGE -> MESSAGE
public class ImageMessage extends Message {
#SerializedName("attachment")
#Expose
private Attachment attachment;
public Attachment getAttachment() {
return attachment;
}
public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
}
Trying to deserialize Message this way with GSon leads (naturally) to empty fields at text or attachment fields. I would like to have a best fit deserialization, which based on response would choose at run time which Message type (i.e. Text or Image) matches most fields to be fulfilled.
So far the only thoughts I had were:
1 - Use #JsonAdapter -> Didn't work
2 - Create another hierarchy structure to point classes at compile time like:
---- Response
|
- TextResponse -> List<TextMessage>
|
- ImageResponse -> List<ImageMessage>
Second option is not really what I want and makes me multiply the number of classes in a way that might get too complicated to apply later maintenance.
Does anyone know a way to handle this problem? Any framework or concepts that could be applied?
Thanks in advance
Maybe you could use Gson extras RunTimeTypeAdapterFactory. Check this example:
RuntimeTypeAdapterFactory<Message> factory = RuntimeTypeAdapterFactory
.of(Message.class, "type") // actually type is the default field to determine
// the sub class so not needed to set here
// but set just to point that it is used
// assuming value 1 in field "int type" identifies TextMessage
.registerSubtype(TextMessage.class, "1")
// and assuming int 2 identifies ImageMessage
.registerSubtype(ImageMessage.class, "2");
Then use GsonBuilder.registerTypeAdapterfactory(factory) to use this.
This just is not found from Gson core library. You need to fetch it here. You might also find some Maven/Gradle dep from global repo that someone has done but maybe easiest is just to copy this file.
It enables later hacks then if you need to modify its behavior.
I implemented this with GodClass that has all of message types fields.
but you don't use this POJO classes as DTO (Data Transfer Object) in your application.
Json is a protocol and not supported Inheritance and etc.
In same scenario I implemented this inheritance and hierarchy for DTOs.
PS: DTO in my answer is Models that we pass for example in Adapter or an Activity and so on.
For some reasons, I need a new object to wrap the data I get from other API.
The problem I faced was that no idea to handle recursive field from the original object.
Here is the sample code:
//original object
public class Resource{
private String name;
private String content;
private Integer type;
private List<Resource> children = new LinkedList();
public void addChildrenResource(Resource children) {
this.children.add(children);
}
//getters&setters...
}
//the object try to convert
public class Menu{
private String name;
private String url;
private Integer type;
private List<Menu> subMenu = new LinkedList();
public void addChildrenResource(Menu subMenu) {
this.children.add(subMenu);
}
//getters&setters...
}
The implementation I did that I have no idea to do with recursive field...
Here is my code..
List<Resource> resources = rpcService.getResources();
//Here I can't handle the subMenu or children part..
List<Menu> menus = resources.stream().map(r -> new Menu(r.getName(),
r.getContent(), r.getType())).collect(Collectors.toList());
The recursion may be many layers, so how can I convert it with recursive field?
Note: The Resource class is from another module so I can't change that, for naming problem I must convert it.
We don't have to solve it by Stream(), just find a way to figure it out.
You need make a method and call this method recursively:
public static List<Menu> convert(List<Resource> resources) {
return resources == null ? null :
resources.stream()
.map(r -> new Menu(r.getName(), r.getContent(), r.getType(), convert(r.getChildren)))
.collect(Collectors.toList());
}
I am working on this test class and trying to change the response expected to a bean response as I have changed the requests to bean requests.
private void assertXmlResponse(Document xmlResponse, int Elements,
String Message, String Code, String name,
String Funds)
{
Node topLevelElement = xmlResponse.getFirstChild();
NodeList childElements = topLevelElement.getChildNodes();
assertEquals("result", topLevelElement.getNodeName());
assertEquals(Elements, childElements.getLength());
assertEquals("message", childElements.item(0).getNodeName());
assertEquals(Message, childElements.item(0).getTextContent());
assertEquals("code", childElements.item(1).getNodeName());
assertEquals(Code, childElements.item(1).getTextContent());
assertEquals("name", childElements.item(2).getNodeName());
assertEquals(name, childElements.item(2).getTextContent());
}
Please can someone point me in the right direction or even let me know if it's possible?
Thanks
You are about to make POJO(Plain Old Java Objects).
public Class A{
private int Elements;
private String Message;
private String Funds;
private String code;
private String name;
//getters and setters
}
Keep the reference of this class as Parameter in your method.
Use the getters for accessing the value in your method.