orika-mapper / orika

Simpler, better and faster Java bean mapping framework
http://orika-mapper.github.io/orika-docs/
Apache License 2.0
1.29k stars 269 forks source link

Null Pointer exception during mapping of complex objects #375

Open mgagauz opened 3 years ago

mgagauz commented 3 years ago

Here is a simple application which tries to convert objects with a nesting level more than one:

public class TestApp {
    public static class ClassA {
        Map<String, String> all;
        public Map<String, String> getAll() {
            return all;
        }
        public void setAll(Map<String, String> all) {
            this.all = all;
        }
    }

    public static class ClassB {
        Map<String, String> section1;
        Map<String, String> section2;
        public Map<String, String> getSection1() {
            return section1;
        }
        public void setSection1(Map<String, String> section1) {
            this.section1 = section1;
        }
        public Map<String, String> getSection2() {
            return section2;
        }
        public void setSection2(Map<String, String> section2) {
            this.section2 = section2;
        }
    }

    public static void main(String[] args) {
        MapperFactory mapperFactory = new DefaultMapperFactory.Builder()
                .captureFieldContext(true)
                .build();
        mapperFactory.classMap(ClassA.class, ClassB.class)
                .field("all['s1prop1']", "section1['prop1']")
                .field("all['s1prop2']", "section1['prop2']")
                .field("all['s2prop1']", "section2['prop1']")
                .field("all['s2prop2']", "section2['prop2']")
                .byDefault()
                .register();
        ClassA source = new ClassA();
        source.setAll(Map.of("s1prop1", "1", "s1prop2", "2"));
        ClassB dest = mapperFactory.getMapperFacade().map(source, ClassB.class);
        System.out.println(dest.getSection1());
        System.out.println(dest.getSection2()); // Should be null
    }
}

Running it will cause NPE during conversion:

Error occurred: java.lang.NullPointerException
    at ma.glasnost.orika.impl.ExceptionUtility.newMappingException(ExceptionUtility.java:55)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:682)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:651)
    at TestApp.main(TestApp.java:55)
Caused by: java.lang.NullPointerException
    at ma.glasnost.orika.generated.Orika_ClassB_ClassA_Mapper821519033837200$0.mapAtoB(Orika_ClassB_ClassA_Mapper821519033837200$0.java)
    at ma.glasnost.orika.impl.mapping.strategy.UseCustomMapperStrategy.map(UseCustomMapperStrategy.java:77)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:672)
    ... 2 more
mgagauz commented 3 years ago

I would propose to change SourceCodeContext

 private void beginCaptureFieldContext(StringBuilder out, FieldMap fieldMap, VariableRef source, VariableRef dest) {
        out.append(format("mappingContext.beginMappingField(\"%s\", %s, %s, \"%s\", %s, %s);\n" + "try{\n",
                escapeQuotes(fieldMap.getSource().getExpression()), usedType(fieldMap.getAType()), source.asWrapper(),
                escapeQuotes(fieldMap.getDestination().getExpression()), usedType(fieldMap.getBType()), dest.asWrapper()));
    }

to

 private void beginCaptureFieldContext(StringBuilder out, FieldMap fieldMap, VariableRef source, VariableRef dest) {
        out.append(format("mappingContext.beginMappingField(\"%s\", %s, %s, \"%s\", %s, %s %s %s %s);\n" + "try{\n",
                escapeQuotes(fieldMap.getSource().getExpression()), usedType(fieldMap.getAType()), source.asWrapper(),
                escapeQuotes(fieldMap.getDestination().getExpression()), usedType(fieldMap.getBType()),
                dest.pathNotNull(), " ? ", dest.asWrapper(), " : null"));
    }
clutcher commented 1 week ago

Looks like orika is not really supported, so SAP Commerce must stop using it and switch on mapstruct.