Open Chr3is opened 5 years ago
There's a strange behaviour while mapping an object instance which was extracted from a list into different objects (with inheritance). In some cases the fields are mapped correctly. This was tested with the lastest Orika version (1.5.4).
Bug1:
Output:
OrikaTest.DestContainer(a=OrikaTest.A(f1=null), b=[OrikaTest.B(super=OrikaTest.A(f1=foo), f2=bar), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=world)])
Bug2:
OrikaTest.DestContainer(a=OrikaTest.A(f1=foo), b=[OrikaTest.B(super=OrikaTest.A(f1=null), f2=null), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=null)])
Expected: OrikaTest.DestContainer(a=OrikaTest.A(f1=foo), b=[OrikaTest.B(super=OrikaTest.A(f1=foo), f2=bar), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=world)])
OrikaTest.DestContainer(a=OrikaTest.A(f1=foo), b=[OrikaTest.B(super=OrikaTest.A(f1=foo), f2=bar), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=world)])
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Assert; import org.junit.Test; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import ma.glasnost.orika.MapperFactory; import ma.glasnost.orika.impl.ConfigurableMapper; import ma.glasnost.orika.impl.DefaultMapperFactory; public class OrikaTest { @Data @AllArgsConstructor public static class Container { private List<Source> source; public Source getFirst() { return source.get(0); } } @Data @AllArgsConstructor public static class Source { private SourceAttributes attributes; } @Data @AllArgsConstructor public static class SourceAttributes { private String f1; private String f2; } @Data public static class DestContainer { private A a; private List<B> b = new ArrayList<>(); } @Data public static class A { private String f1; } @EqualsAndHashCode(callSuper = true) @Data @ToString(callSuper = true) public static class B extends A { private String f2; } public static class ExampleMapper extends ConfigurableMapper { private final int bug; public ExampleMapper(int bug) { super(false); this.bug = bug; init(); } @Override protected void configureFactoryBuilder(DefaultMapperFactory.Builder factoryBuilder) { factoryBuilder.useAutoMapping(false); } @Override protected void configure(MapperFactory factory) { if (bug == 1) { factory.classMap(Container.class, DestContainer.class) // switching these fields causes // OrikaTest.DestContainer(a=OrikaTest.A(f1=null), b=[OrikaTest.B(super=OrikaTest.A(f1=foo), f2=bar), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=world)]) .field("source", "b") .field("first", "a") .register(); } else { factory.classMap(Container.class, DestContainer.class) .field("first", "a") .field("source", "b") .register(); } factory.classMap(Source.class, A.class) .field("attributes", "") .register(); if (bug != 2) { // removing this causes // OrikaTest.DestContainer(a=OrikaTest.A(f1=foo), b=[OrikaTest.B(super=OrikaTest.A(f1=null), f2=null), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=null)]) factory.classMap(Source.class, B.class) .field("attributes", "") .register(); } factory.classMap(SourceAttributes.class, A.class) .byDefault() .register(); factory.classMap(SourceAttributes.class, B.class) .byDefault() .register(); } } private Container container = new Container(Arrays.asList(new Source(new SourceAttributes("foo", "bar")), new Source(new SourceAttributes("hello", "world")))); @Test public void testOkMapping() { ExampleMapper mapper = new ExampleMapper(0); DestContainer destContainer = mapper.map(container, DestContainer.class); doAssertions(destContainer); } @Test public void testBug1() { ExampleMapper mapper = new ExampleMapper(1); DestContainer destContainer = mapper.map(container, DestContainer.class); doAssertions(destContainer); } @Test public void testBug2() { ExampleMapper mapper = new ExampleMapper(2); DestContainer destContainer = mapper.map(container, DestContainer.class); doAssertions(destContainer); } private void doAssertions(DestContainer destContainer) { System.out.println(destContainer); Assert.assertNotNull(destContainer.getA()); Assert.assertNotNull(destContainer.getA().getF1()); Assert.assertNotNull(destContainer.getB()); Assert.assertNotNull(destContainer.getB().get(0).getF1()); Assert.assertNotNull(destContainer.getB().get(0).getF2()); Assert.assertNotNull(destContainer.getB().get(1).getF1()); Assert.assertNotNull(destContainer.getB().get(1).getF2()); } }
A possible solution is to use the BoundMapperFacade with the "containsCycle=false" option.
There's a strange behaviour while mapping an object instance which was extracted from a list into different objects (with inheritance). In some cases the fields are mapped correctly. This was tested with the lastest Orika version (1.5.4).
Bug1:
Output:
OrikaTest.DestContainer(a=OrikaTest.A(f1=null), b=[OrikaTest.B(super=OrikaTest.A(f1=foo), f2=bar), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=world)])
Bug2:
Output:
OrikaTest.DestContainer(a=OrikaTest.A(f1=foo), b=[OrikaTest.B(super=OrikaTest.A(f1=null), f2=null), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=null)])
Expected:
OrikaTest.DestContainer(a=OrikaTest.A(f1=foo), b=[OrikaTest.B(super=OrikaTest.A(f1=foo), f2=bar), OrikaTest.B(super=OrikaTest.A(f1=hello), f2=world)])