Search code examples
xstream

XStream get parent object in converter


For a local converter, when marshalling to XML, is there a way to access the parent object ?

I need to marshall a collection with items from a third-party source - using an id stored in the parent object.

Alas, there seem to be no way to query the object path leading to the current object. Or is there ?


Solution

  • I found a solution with little bit reflaction:

    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.util.List;
    import java.util.Map;
    
    import org.junit.Test;
    import org.springframework.util.ReflectionUtils;
    
    import com.thoughtworks.xstream.XStream;
    import com.thoughtworks.xstream.annotations.XStreamAlias;
    import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
    import com.thoughtworks.xstream.converters.Converter;
    import com.thoughtworks.xstream.converters.MarshallingContext;
    import com.thoughtworks.xstream.converters.UnmarshallingContext;
    import com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller;
    import com.thoughtworks.xstream.io.HierarchicalStreamReader;
    import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
    import com.thoughtworks.xstream.io.path.Path;
    
    import lombok.Data;
    
    public class XStreamGetInfoFromParentTest {
    
        @Test
        public void smokeTest() {
            InputStream file = XStreamGetInfoFromParentTest.class.getResourceAsStream("XStreamGetInfoFromParentTest.xml");
    
            XStream xStream = new XStream() {
                @Override
                public void registerConverter(Converter converter, int priority) {
                    Converter myConverter = new MyConverterWrapper(converter);
                    super.registerConverter(myConverter, priority);
                }
            };
            xStream.processAnnotations(Papa.class);
            xStream.processAnnotations(Baby.class);
    
            Papa papa = (Papa) xStream.fromXML(file);
    
            System.out.println(papa);
        }
    
        public class MyConverterWrapper implements Converter {
    
            private Converter converter;
            private boolean isBabyClass;
    
            public MyConverterWrapper(Converter converter) {
                this.converter = converter;
            }
    
            @Override
            public boolean canConvert(Class type) {
                this.isBabyClass = type.equals(Baby.class);
                return converter.canConvert(type);
            }
    
            @Override
            public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
                this.converter.marshal(source, writer, context);
            }
    
            @SuppressWarnings({ "unchecked", "rawtypes" })
            @Override
            public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
                if (isBabyClass) {
                    AbstractReferenceUnmarshaller runm = (AbstractReferenceUnmarshaller) context;
    
                    Field field = ReflectionUtils.findField(AbstractReferenceUnmarshaller.class, "values");
                    field.setAccessible(true);
                    Map values = (Map) ReflectionUtils.getField(field, runm);
    
                    values.forEach((key, value) -> {
                        System.out.println(key + " : " + value);
                    });
    
                    Papa papa = (Papa) values.get(new Path("/papa"));
                    System.out.println(papa.firstname);
                }
    
                return converter.unmarshal(reader, context);
            }
    
        }
    
        @XStreamAlias("papa")
        @Data
        public class Papa {
    
            @XStreamAsAttribute
            private String firstname;
    
            private int age;
    
            private List<Baby> babies;
    
        }
    
        @XStreamAlias("baby")
        @Data
        public class Baby {
            private String firstname;
        }
    
    }
    

    xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <papa firstname="Adam">
            <age>33</age>
            <babies>  
                <baby>
                    <firstname>Eva</firstname>
                </baby>
            </babies>
    </papa>