0

Let's say I have a 10 unique POJOs each with 10 uniquely named fields.

This POJO is a deserialized SOAP response. I want to "copy" the fields from the SOAP POJOs over to a POJO that represents a JSON object, and then serialize that POJO.

Let's name our SOAP objects o0 ... o9
Let's name our SOAP fields f0 ... f9

Let's name our JSON object jo
Let's name our JSON fields f0 ... f99

Finally, all fields are containers for values, so each field has a .getValue() method.

Assuming all our fields have getters and setters, the only way to do this, from my understanding, is hardcoding the following:

jo.setF0(o0.getF0().getValue());
jo.setF1(o0.getF1().getValue());
...
jo.setF49(o4.getF9().getValue());
...
jo.setF99(o9.getF9().getValue());

Now, the problem is that any of the fields belonging to o0 ... o9 MAY be null, which will cause a NullPointerException.

Is there any way to get around this without writing 100 try/catch blocks?

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
Phil Liu
  • 11
  • 2
  • Hi, if something is null, then it is null. which means that the new object you want to create must be able (allow) null in some of it;s properties. You can check for null in the setFXXX() and handle it accordingly. If you can not change anything else on the source and target domain model – javapapo Jan 11 '16 at 15:40
  • I get null NullPointerException from invoking null.getValue(), not because obj.getValue() == null. – Phil Liu Jan 11 '16 at 17:06

2 Answers2

0

maybe you can prevent nullPointer, testing if object is null before set value.

o0.getF0!=null ? o0.getF0().getValue(): null;

take a look to some framework like https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html to copy properties.

ZaoTaoBao
  • 2,567
  • 2
  • 20
  • 28
  • This is the equivalent of writing try/catch blocks, since it is still not general case. The BeanUtils property copy doesn't match my use case, as there is no guarantee that the fields are the same structure. For example, `soapresponse/body/user/id` may get copied to `jsonresponse/userid`. – Phil Liu Jan 11 '16 at 17:11
  • well and if you allow nullable properties ? http://stackoverflow.com/questions/14076296/nullable-annotation-usage – ZaoTaoBao Jan 11 '16 at 17:25
0

Here is a naive implementation of e bean value copier that copies all non-null properties that are present in both source and target:

public class BeanValueCopier {

    static final Map<Class<?>, Map<String, PropertyDescriptor>> beanPropertiesByType = new ConcurrentHashMap<>();

    public static void copyNonNullValues(Object src, Object dest) {
        try {
            Map<String, PropertyDescriptor> sourceMap = propertyMap(src.getClass());
            Map<String, PropertyDescriptor> targetMap = propertyMap(dest.getClass());
            for (Map.Entry<String, PropertyDescriptor> entry : sourceMap.entrySet()) {
                final String propertyName = entry.getKey();
                if (targetMap.containsKey(propertyName)) {
                    final PropertyDescriptor sourceDesc = entry.getValue();
                    final PropertyDescriptor targetDesc = targetMap.get(propertyName);
                    if (targetDesc.getPropertyType().equals(sourceDesc.getPropertyType()) && sourceDesc.getReadMethod() != null && targetDesc.getWriteMethod() != null) {
                        final Object value = sourceDesc.getReadMethod().invoke(src);
                        if (value != null) targetDesc.getWriteMethod().invoke(dest, value);
                    }
                }
            }
        } catch (InvocationTargetException | IllegalAccessException | IntrospectionException e) {
            throw new IllegalStateException(e);
        }
    }

    private static Map<String, PropertyDescriptor> propertyMap(Class<?> beanClass) throws IntrospectionException {
        Map<String, PropertyDescriptor> beanProperties = beanPropertiesByType.get(beanClass);
        if (beanProperties == null) {
            beanProperties = new HashMap<>();
            for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(beanClass, Object.class).getPropertyDescriptors()) {
                beanProperties.put(propertyDescriptor.getName(), propertyDescriptor);
            }

            beanPropertiesByType.put(beanClass, beanProperties);
        }
        return beanProperties;
    }
}

Obviously this still has a lot of issues: Concurrency, handling of primitive types, exception handling etc., but it should get you started

This uses the JavaBeans Introspector mechanism

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • The BeanUtils property copy doesn't match my use case, as there is no guarantee that the fields are the same structure. For example, `soapresponse/body/user/id` may get copied to `jsonresponse/userid`. There is also the concern of a 10:1 soapPOJO:jsonPOJO ratio. – Phil Liu Jan 11 '16 at 17:13
  • @PhilLiu Too bad. Perhaps you should have mentioned that in your question – Sean Patrick Floyd Jan 12 '16 at 06:47
  • Then perhaps a property mapper framework like Dozer is the right thing for you – Sean Patrick Floyd Jan 12 '16 at 06:48