-2

I have a long list of object mapping to do from classes auto generated by JAXB.

 customer.setCustomerId(rentalCustomer.getCustomerid().getValue()));
 customer.setCustomerName(rentalCustomer.getTradingname().getValue());
 customer.setVatNumber(rentalSearchCustomer.getVatNumber().getValue());
 ....
 ....

Basically I need to make a null check for ALL fields:

getValue(RentalCustomerIDType idType){
  if(idType != null){
    return idType.getValue();
  }
  else {
   return "";
 }
}

Problem is there are too many of these and they all have different types: RentalCustomerIDType, TradingType, VatNumberType..etc

Is there an elegant way to this by creating a GENERIC method that makes null check and return proper values for ALL maybe using Functional Libraries for Java?

Spring
  • 11,333
  • 29
  • 116
  • 185
  • 1
    is `idType.getValue()` always going to be a String? Or could it also be other types? – Charlie Armstrong Jan 12 '21 at 22:19
  • @CharlieArmstrong always string – Spring Jan 12 '21 at 22:20
  • 1
    Ok, then we just need to find something that is in common about all of these methods you want to call. Do these types all extend a certain class or implement a certain interface? Are all of the methods named `getValue()`? Otherwise we just have to write out a big long switch-case statement. – Charlie Armstrong Jan 12 '21 at 22:27
  • Use reflection, populate nulls with a default value and use the Class.newInstance() method for non-primitives. (You can test if it's a primitive or not with the reflection api) –  Jan 12 '21 at 23:55
  • 1
    @Spring I would suggest not screaming at the people who are trying to help you and are asking clarifying questions in order to do so. – CheeseFerret Jan 13 '21 at 18:34
  • 2
    OK, a point of confusion. Do all getValue() method's return a String? If not, what do they return. And what are examples of the classes that house the getValue() method. – WJS Jan 13 '21 at 22:39
  • @WJS I really appreciate your interest on helping. but it looks like not possible without using Reflection. getvalue() is always string. but each type has its own getValue() method. so its not one method. – Spring Jan 14 '21 at 12:54
  • 1
    @Spring It just dawned on me what the issue is. Sorry about my persistence. But this is an interesting problem so I am going to continue looking at it. But I have one simple question? Did you write classes for each type that has the getValue() method? Or does each type implement some interface that specifies the `getValue` method? – WJS Jan 14 '21 at 14:27
  • @WJS it’s jaxb created – Spring Jan 15 '21 at 16:01
  • @Spring Thanks. I've exhausted my ideas. If you can get the Interface or can specify one for getValue(), my solution should work. Otherwise you will have to use reflection. Regards!! – WJS Jan 15 '21 at 17:46

6 Answers6

1

Perhaps use reflection on the class when it's generated and eliminate all nulls by assigning non-null values to the fields?

Check an replace null values in multiple variables java

They say (the guy who answered) that they strongly disagree with using reflection for this purpose... but... meh. I've done it and it works.

1

You could use a generic method to declare the getValueFromAllObjects method and then use reflection to invoke the getValue method

public static <T> String getValueFromAllObjects(T t) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
  if(t != null){
    return (String) t.getClass().getDeclaredMethod("getValue").invoke(t);
  }
  else {
   return "";
 }
}

Refer to https://stackoverflow.com/a/54883454/442256 for reflection alternatives. I've just inlined an example in your code above

sfk
  • 625
  • 1
  • 9
  • 17
  • The OP said *pls notice getValue() is specific for type*. So getValue() returns different types. You would need one of these methods for each type. You can't assign a String to all types. – WJS Jan 13 '21 at 22:06
  • 1
    I've assumed it is String as that what he's clarified in the comment [here] (https://stackoverflow.com/questions/65692652/how-to-null-check-and-return-a-value-in-a-generic-way/65693438?noredirect=1#comment116149551_65692652) – sfk Jan 13 '21 at 22:20
  • That was for `idType`. There are other types. – WJS Jan 13 '21 at 22:38
  • 1
    @WJS the return of an empty String "" in the getValue() function implies String – sfk Jan 14 '21 at 07:12
  • @sfk thx sfk. Didnt test but I think this is the only answer here that can get he job done. But unfortunately not very reluctant to use reflections,,but I apprecciate your answer +1 – Spring Jan 14 '21 at 12:56
  • 1
    @Spring no worries, if it helps arguing the case "for" reflection, JAXB uses it quite heavily and so do DI frameworks such as Spring – sfk Jan 14 '21 at 16:30
0

From what I understand, you do not want to be changing existing auto-generated classes. What you can do is create a CustomerWrapper class that wraps Customer and inserts defaults when a null is set to a field. This is the idea in code:

public class CustomerWrapper() {

    private final Customer customer;

    public CustomerWrapper(Customer customer) {
        this.customer = customer;
    }

    public void setCustomerId(String id) {
        this.customer.setCustomerId(id == null ? "" : id);
    }

    // Insert other methods here.

}
Pieter12345
  • 1,713
  • 1
  • 11
  • 18
  • writing a wrapper that handles all different types for all fields is not more elegant then checking nulls – Spring Jan 12 '21 at 22:14
0

This does what you want. But there is a caveat. Each type must implement the same interface that provides the getValue() method. Otherwise, you will probably need reflection to get the method as you suspected. But here is a solution for posterity.

setType(rentalSearchCustomer::getCustomerid,
        customer::setCustomerId);
setType(rentalSearchCustomer::getTradingname,
        customer::setCustomerName);
setType(rentalSearchCustomer::getVatNumber,
        customer::setVatNumber);
System.out.println(customer);
        
    
    
public static <T extends GetValue> void setType(Supplier<T> sup,
        Consumer<String> con) {
    if (sup.get() == null) {
        con.accept("");
    } else {
        con.accept(sup.get().getValue());
    }
}
    
interface GetValue {
    public String getValue();
}
WJS
  • 36,363
  • 4
  • 24
  • 39
  • I think you misunderstood the question.Your example shows optional accepts a string how do you send RentalSearchCustomerIDType to your getValue method? and imagine that type is different for all fields – Spring Jan 12 '21 at 22:16
  • thx but its missing the getValue() method of my custom type object pls notice getValue() is specific for type – Spring Jan 12 '21 at 22:45
0

I guess you want to use something like. here I have taken ResponseUserDto as my Pojo Class for null checks of it's properties.

private ResponseUserDto getValidNotNullPropertyObject(Object source) {
    final BeanWrapper src = new BeanWrapperImpl(source);
    Map < String, Object > result = new HashMap<>();
    for (PropertyDescriptor property: src.getPropertyDescriptors()) {
        if (src.getPropertyValue(property.getName()) == null) {
            /* if(property.getPropertyType() == ?) {
                    //maybe do somethig here
                }*/
            result.put(property.getName(), ""); // this is start
        } else {
            result.put(property.getName(), src.getPropertyValue(property.getName()));
        }
    }
    final ObjectMapper mapper = new ObjectMapper(); // Jackson's ObjectMapper
    final ResponseUserDto finalResult = mapper.convertValue(result, ResponseUserDto.class);
    return finalResult;
}

and to use this, you can call it like this

return this.getValidNotNullPropertyObject(responseUserDto);
mss
  • 1,423
  • 2
  • 9
  • 18
0

maybe a case for Aspect Oriented Programming, if its use is an option: