Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XStream get parent object in converter

Tags:

xstream

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 ?

like image 680
Jan Goyvaerts Avatar asked Nov 21 '22 20:11

Jan Goyvaerts


1 Answers

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>
like image 179
Mark Avatar answered May 09 '23 20:05

Mark