convert Object and wrap to another with recursive field in java - java

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

Related

Mapping Hierarchical data in java from source object to target object

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

Can I map between values inside enums?

I create enum which has two values: brand name and brand code.
I want to know the brand code by inputting the brand name.
And I also want to know the brand name by inputting the brand code.
Can this problem solved using Enum? or other code is more effective? I want to make the code as shorter as possible
I have created following code to search the code of a brand. If I want to do vice versa, I can create another Hashmap and method to convert the code into a brand. But is that the effective way to solve it?
public enum Brand{
COLA("cola", "CL8935"),
BREAD("bread", "BR2810"),
SNICKERS("snickers", "SN4423");
private static final Map<String, String> BY_BRAND = new HashMap<>();
static {
for (Brand brand : values()){
BY_BRAND.put(brand.code, brand.brand);
}
}
private final String brand;
private final String code;
public static String convertToCode(String brand){
return BY_BRAND.get(brand.toLowerCase()).toString();
}
}
Update - Adding the full enum (with imports)
import java.util.Arrays;
import java.util.function.Function;
enum Brand {
COLA("cola", "CL8935"),
BREAD("bread", "BR2810"),
SNICKERS("snickers", "SN4423");
private final String brand;
private final String code;
Brand(String brand, String code) {
this.brand = brand;
this.code = code;
}
public static Brand findBy(String value, Function<Brand, String> extractor) {
return Arrays.stream(Brand.values())
.filter(brand -> extractor.apply(brand).equalsIgnoreCase(value))
.findFirst()
.orElse("Either a default or throw exception here");
}
public String getBrand() {
return brand;
}
public String getCode() {
return code;
}
}
Original
You could use a static findBy method as an alternative to the map. This would allow you to pass in the value and method reference for the getter which will be used to compare the values stored within the enum.
The difference here would be performance (as maps would be faster), the fact that you would be returning the enum and that you most likely would want either a default enum value or to throw an exception on no matched being found. Below is an example
public static Brand findBy(String value, Function<Brand, String> extractor) {
return Arrays.stream(Brand.values())
.filter(brand -> extractor.apply(brand).equalsIgnoreCase(value))
.findFirst()
.orElse("Either a default or throw exception here");
}
And this can be called like this
public static void main(String[] args) {
Brand brand1 = Brand.findBy("cola", Brand::getBrand);
Brand brand2 = Brand.findBy("BR2810", Brand::getCode);
}
Simple static method in Brand should do:
public static String getBrand(String code) {
for(Brand b : Brand.values()){
if(b.getCode().equals(code)) return b.getBrand();
}
return null;
}
Similarly you can write a getCode(String brand)
Edit: assuming the two attributes do not have the same value, you can check do the bi-di mapping in the same method:
public static String getOtherAttribute(String value) {
for(Brand b : Brand.values()){
if(b.getCode().equals(value)) return b.getBrand();
if(b.getBrand().equals(value)) return b.getCode();
}
return null;
}
If the two attributes may have the same value you can add an argument (flag) to the method's signature to tell which attribute you want to retrieve.

java ArrayList is adding just one element

I am creating a JavaFXML application and I need to have these classes there: ListOfAllEvents, Events and Instructors. ListOfAllEvents has an ArrayList of Events, and each Event has an ArrayList of Instructors.
Now, clicking on buttons and using methods for creating instances I first create several Instructors and add them to the ArrayList CurrentInstructors, then I create an instance of Event, assign the list of instructors to it and then I would like to add my new Event to the ArrayList CurrentEvents.
My problem is, CurrentEvents does add only one Event, and when I call the method for creating new Event for second time, the second event replaces the first one, even though my CurrentInstructors work correctly.
Can you help me? I am a beginner at Java, so I'll be thankful for every piece of advice.
My code:
public class FXMLDocumentController implements Initializable {
(...)
private ArrayList<Instructor> CurrentInstructors = new ArrayList<Instructor>();
private ArrayList<Event> CurrentEvents = new ArrayList<Event>();
#FXML
private void AddInstructor_Click(ActionEvent event) {
TextInputDialog dialog = new TextInputDialog("");
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
Instructor newInstructor = new Instructor(result.get());
CurrentInstructors.add(newInstructor);
}
}
private void NewEvent () {
Event newEvent = new Event(EventName.getText());
//this is the problematic row:
CurrentEvents.add(newEvent);
newEvent.setInstructors(CurrentInstructors);
CurrentInstructors.clear();
}
(...)
}
Instructor class:
public class Instructor {
private String name;
public String getName() {
return name;
}
public Instructor(String name){
this.name = name;
}
}
Event Class:
public class Event {
private String name;
private ArrayList<Instructor> Instructors = new ArrayList<Instructor>();
public Event(String name){
this.name = name;
}
//getters, setters
}
You only have one list which you are setting, then immediately emptying.
newEvent.setInstructors(CurrentInstructors);
CurrentInstructors.clear(); // This clears the list just set to newEvent
Since Java is pass-by-value, you have multiple references to a single List object
If you want each new event to hold it's own list, you need to explicitly make new ArrayList<>() each newEvent object made, then add to that, and call setInstructors
For example,
private void newEvent(String name) {
Event newEvent = new Event(name);
// Copy the list
List<Instructor> instructors = new ArrayList<>(currentInstructors);
// Set that list
newEvent.setInstructors(instructors);
currentEvents.add(newEvent);
// Now clearing this list won't clear the newEvent list
currentInstructors.clear();
}

iterate through the objects of a class

I have created this class.
public class UserData {
private int user_Id;
private List<String> disease_List=new LinkedList<String>();
/*Constructor*/
public UserData(int u_id, List<String> d_list)
{
this.user_Id=u_id;
this.disease_List=d_list;
}
}
I created around 500 objects by reading data from file.
Now I want to search for values. For example If user enters a disease= allergy.
I want to iterate through all objects and display users that have allergy in disease_list.
I have searched on internet for this but have not found anything useful.
You have to add the userData objects in a collection and iterate them and access it's attributes say disease_list.
For example, if you've added the userData objects in a arrayList, you can iterate in the following way
for(UserData user:UserDataList){
if(user.disease_list.contains("allergy") {
//display the user details
}
}
I might have misunderstood the question.. But basically what you want to do is search for a certain keyword in the disease_List inside every UserData object, right?
So say you have added a String allergy = "allergy"; inside the disease_list.
What you want to do in order to find this you need to iterate through the disease_list instance.
for (int i = 0; i < disease_list.size(); i++) {
if(disease_list.get(i).equals("allergy")){
//Now you know this UserData object contains "allergy". Now you
//can choose to break the iteration and do something with
//the object.
}
}
You can use Lambdas expression (with java 8 or higher) to sort lists.
public class UserData {
private int user_Id;
private ArrayList<String> diseases;
/*Constructor*/
public UserData(int u_id, ArrayList<String> d_list)
{
this.user_Id=u_id;
this.diseases=d_list;
}
public ArrayList filter(String disease) {
return Arrays.stream(diseases).filter(x -> x.equal(disease)).toArray();
}
}
Java doesn't just automatically make a list of every object you create, you need to make the list yourself.
public class UserData {
/* This must exist somewhere */
private static List<UserData> everyone = new LinkedList<UserData>();
private int user_Id;
private List<String> disease_List=new LinkedList<String>();
/*Constructor*/
public UserData(int u_id, List<String> d_list)
{
this.user_Id=u_id;
this.disease_List=d_list;
everyone.append(this);
}
/* Iterator for all the objects */
public static Iterator<UserData> iterator() {
return everyone.iterator();
}
}
if you want to iterate here is one way
for(String disease:disease_list){
if(disease.equalsIgnoreCase("allergy")
log.info("You find out the Result");
}
else you can use contains() method
if(disease_list.contains("allergy")){
log.info("found the disease");
}

Duplicate and add fields between classes

I was wondering if the following scenario is possible.
Having two classes (Source and Destination) where in code I could do this:
public class Source{
private String fieldA;
private String fieldB;
public Source(){ ... }
}
...
public class Destination{
public Destination(Source src){ ... }
}
Source src = new Source();
Destination dest = new Destination(src);
dest.fieldA = "test";
dest.fieldB = "test";
So what I mean here is that I have two classes, one called Source that contains (private) fields and one called Destination with no fields. After creating two objects of these classes and passing in Source into the constructor of Destination, I want to be able to duplicate/copy the fields of Source into Destination.
Could something like this be possible in Java, whether or not using Reflection? And if possible, can someone give me a minor example I can start with.
A hackish version to accomplish this is to add all fields to a Map. The fields can be copied from the source object to the destination object and the field name can be the key. Something along the lines of this:
public class FieldAccessor {
public static class Destination {
private final Map<String, Object> fields = new HashMap<>();
public Destination(Object o) {
final Set<Field> accessibleFields = Arrays.stream(o.getClass().getDeclaredFields())
.map(field -> {
field.setAccessible(true);
return field;
})
.collect(Collectors.toSet());
accessibleFields.forEach(field -> {
try {
fields.put(field.getName(), field.get(o));
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access field", e);
}
});
}
public Set<String> fieldNames() {
return fields.keySet();
}
public Optional<Object> fieldValue(String fieldName) {
return Optional.ofNullable(fields.get(fieldName));
}
}
public static class Source {
private final String fieldA;
private final Integer fieldB;
private final int fieldC;
public Source(String fieldA, Integer fieldB, int fieldC) {
this.fieldA = fieldA;
this.fieldB = fieldB;
this.fieldC = fieldC;
}
public String getFieldA() {
return fieldA;
}
public Integer getFieldB() {
return fieldB;
}
public int getFieldC() {
return fieldC;
}
}
#Test
public void testFields() {
Destination destination = new Destination(new Source("Abc", 123, 456));
destination.fieldNames().stream().forEach(fieldName -> {
System.out.println("Fieldname: " + fieldName + ", value: " + destination.fieldValue(fieldName).get());
});
}
}
For more info, check out this SO.
However, this is not something I would use in real production code. Instead, I would use some sort of serialization by e.g. using Jackson.
So you want to dynamically create fields in an object ? That's not directly possible in Java. If you just wanted to copy methods of an interface, the answer would have be to use a JDK proxy. It may still be of interest if :
you accept to only use getters and setters in Destination class
Source class implements an interface defining the setters and getters you want to copy
If you cannot accept those restrictions, you will have to look to CGLIB proxies or Javassist, that is libraries that dynamically modify the bytecode of the compiled class objects at load time. It is a really advanced feature, that is mainly used in frameworks or other libraries and not in high-level programs. Typically it is used in Object Relational Mappers like Hibernate to replace simple collection classes with enhanced ones that transparently gets (an save) their elements in database.
In any other case, trying to access private fields outside of the class should be seen as an indicator for a possible design flaw. private means implementation dependant and can change across versions and should not be used without knowing why.
The simplest and most efficient way to do it is copying the fields explicitly :
public Destination(Source src)
{
this.fieldA = src.getFieldA();
this.fieldB = src.getFieldB();
}
I don't see the point in using reflection for this purpose.
The only thing is in my mind for this at this time is extending Destination class from Source
public class Source{
private String fieldA;
private String fieldB;
//You need to have both Getter and Setter for fieldA and fieldB
public Source(){ ... }
}
...
public class Destination extends Source{
public Destination(){...}
}
Source src = new Destination();
dest.setFieldA("test");
dest.setFieldB("test");
Private members of Source cannot be accessed from Destination object even if you are passing a Source object to Destination.
You need to add string fieldA, fieldB to Destination to
string fieldA, fieldB;
public Destination(Source src)
{
fieldA = src.fieldA;
fieldB = src.fieldB;
}

Categories

Resources