How do I, using XStream, serialize/deserialize objects in a type hierarchy?

dennislloydjr

I am using XStream. At the moment it is not easy to replace XStream with something else.

I have an interface (MyInterface) and several sub-classes which implement that interface (in the sample code below, there is one called MyImplementation).

I want to serialize and deserialize instances of the sub-classes. I found that I can deserialize just fine if I put the class attribute into the XML:

<myInterfaceElement class="myPackage.MyImplementation">
  <field1>value1</field1>
  <field2>value2</field2>
</myInterfaceElement>

However, I do not know how to get XStream to write the class attribute. How can I get XStream to include the class attribute when serializing? Or is there another way to serialize/deserialize a class hierarchy so that the element name is the same for all implementations and each subclass can have their own fields defined?

Here is an example of MyInterface, MyImplementation, a JUnit test case trying to make it work. The deserializeWithClassAttribute test passes while the classAttributeSetInResult fails.


package myPackage;

public interface MyInterface {

}

package myPackage;

public class MyImplementation implements MyInterface {
    public String field1;
    public String field2;

    public MyImplementation(String field1, String field2) {
        this.field1 = field1;
        this.field2 = field2;
    }
}

package myPackage;
import org.junit.Test;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

import static org.junit.Assert.*;


public class xstreamTest {
    @Test
    public void classAttributeSetInResult() {
        MyInterface objectToSerialize = new MyImplementation("value1", "value2");

        final XStream xStream = new XStream(new DomDriver());
        xStream.alias("myInterfaceElement", MyInterface.class);

        String xmlResult = xStream.toXML(objectToSerialize).toString();

        String expectedResult = 
"<myInterfaceElement class=\"myPackage.MyImplementation\">\n" +
"  <field1>value1</field1>\n" +
"  <field2>value2</field2>\n" +
"</myInterfaceElement>";

        assertEquals(expectedResult, xmlResult);
    }

    @Test
    public void deserializeWithClassAttribute() {
        String inputXmlString = 
"<myInterfaceElement class=\"myPackage.MyImplementation\">\r\n" +
"  <field1>value1</field1>\r\n" +
"  <field2>value2</field2>\r\n" +
"</myInterfaceElement>";

        final XStream xStream = new XStream(new DomDriver());

        MyInterface result = (MyInterface)xStream.fromXML(inputXmlString);
        assertTrue("Instance of MyImplementation returned", result instanceof MyImplementation);

        MyImplementation resultAsMyImplementation = (MyImplementation)result;
        assertEquals("Field 1 deserialized", "value1", resultAsMyImplementation.field1);
        assertEquals("Field 2 deserialized", "value2", resultAsMyImplementation.field2);
    }
}
dennislloydjr

I figured this out by doing the following (thanks to McD on the hint to use a Converter):

  1. Add a custom Converter that extends ReflectionConverter:

    package myPackage;
    
    import com.thoughtworks.xstream.converters.MarshallingContext;
    import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
    import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
    import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
    import com.thoughtworks.xstream.mapper.Mapper;
    
    public class MyInterfaceConverter extends ReflectionConverter {
    
        public MyInterfaceConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
            super(mapper, reflectionProvider);
        }
    
        @Override
        public void marshal(Object original, final HierarchicalStreamWriter writer, final MarshallingContext context) {
            writer.addAttribute("class", original.getClass().getCanonicalName());
            super.marshal(original, writer, context);
        }
    
        @SuppressWarnings("rawtypes")
        @Override
        public boolean canConvert(Class type) {
            return MyInterface.class.isAssignableFrom(type);
        }
    
    }
    
  2. Registering the new Converter when I setup xStream:

    @Test
    public void classAttributeSetInResult() {
        MyInterface objectToSerialize = new MyImplementation("value1", "value2");
    
        final XStream xStream = new XStream(new DomDriver());
        xStream.alias("myInterfaceElement", MyImplementation.class);
        xStream.registerConverter(new MyInterfaceConverter(xStream.getMapper(), xStream.getReflectionProvider()));
    
        String xmlResult = xStream.toXML(objectToSerialize).toString();
    
        String expectedResult = 
            "<myInterfaceElement class=\"myPackage.MyImplementation\">\n" +
            "  <field1>value1</field1>\n" +
            "  <field2>value2</field2>\n" +
            "</myInterfaceElement>";
    
        assertEquals(expectedResult, xmlResult);
    }
    

Hopefully this will help someone else down the road. If anyone has a better idea, please let me know!

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How do I use xStream to output Java Objects with List properties?

How can I register a (boundless) type hierarchy using Autofac?

How do I rename XStream list elements?

XStream: Collapsing XML hierarchy as I parse

How do I de-serialize an attribute and a text node using XStream?

If given a list of objects (files/folders) and their paths as a string, how do I organize them into a formal hierarchy?

How do you show the type hierarchy of a value?

How do generic types fit into the type hierarchy?

how do I get xstream to combine mouse events?

How do I minimize the amount of code for a hierarchy

How do I inspect the view hierarchy in iOS?

How can i select an html element based on type and not hierarchy using only CSS

How do I group by using anonymous type

how do I create a narrow type based on an objects values

How do I define an array of objects with the same parent type?

How do I find objects of a certain type in a Class in C#?

How do I add objects to an Array of type Created class?

How do I specify an objects type when Key is a known value?

xStream problems - How to deserialize multiple objects

How do I sort this Array of objects using Vanilla or lodash

How do I filter an array of objects by keyword using LoDash?

How do I randomly move objects in a maze using pygame?

Using RequireJS, how do I pass in global objects or singletons around?

How do I map a set of string objects using JPA Annotations?

How do I efficiently cache objects in Java using available RAM?

how do I filter an array of objects using multiple parameters

How do I marshall multiple objects using JAXB?

How do i sort Nested JSON Objects using Gson Library?

How do I plot Shapely polygons and objects using Matplotlib?