Search code examples
xstream

XStream: convert collection with attributes


The XML I'm trying to convert looks like:

<numberOfEmployees year="2013">499.0</numberOfEmployees>

According to the XSD, there can be multiple of these tags, so it's a collection. The generated code looks like:

    protected List<NumberOfPersonnel> numberOfEmployees;

When I use @XStreamImplicit, it drops the value, so I need a converter. But combining @XStreamImplicit with @XStreamConverter doesn't seem to work.

So how do I do this? I've tried messing about with my own custom converter that inherits from CollectionConverter, but it claims not to find any children, and honestly I have no idea what I'm doing.

Could someone enlighten me? This shouldn't be this hard, should it?


Solution

  • I can make it work by using ToAttributedValueConverter on the NumberOfPersonnel class and @XStreamImplicit on the List-valued property:

    NumberOfPersonnel.java

    import com.thoughtworks.xstream.annotations.*;
    import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter;
    
    // treat the "value" property as the element content and all others as attributes
    @XStreamConverter(value = ToAttributedValueConverter.class, strings = {"value"})
    public class NumberOfPersonnel {
      public NumberOfPersonnel(int year, double value) {
        this.year = year;
        this.value = value;
      }
    
      private int year;
    
      private double value;
    
      public String toString() {
        return year + ": " + value;
      }
    }
    

    Container.java

    import com.thoughtworks.xstream.XStream;
    import com.thoughtworks.xstream.annotations.*;
    import java.util.List;
    import java.util.Arrays;
    import java.io.File;
    
    @XStreamAlias("container")
    public class Container {
      private String name;
    
      // any element named numberOfEmployees should go into this list
      @XStreamImplicit(itemFieldName="numberOfEmployees")
      protected List<NumberOfPersonnel> numberOfEmployees;
    
      public Container(String name, List<NumberOfPersonnel> noEmp) {
        this.name = name;
        this.numberOfEmployees = noEmp;
      }
    
      public String toString() {
        return name + ", " + numberOfEmployees;
      }
    
      public static void main(String[] args) throws Exception {
        XStream xs = new XStream();
        xs.processAnnotations(Container.class);
    
        System.out.println("Unmarshalling:");
        System.out.println(xs.fromXML(new File("in.xml")));
    
        System.out.println("Marshalling:");
        System.out.println(xs.toXML(new Container("World",
               Arrays.asList(new NumberOfPersonnel(2001, 1000),
                             new NumberOfPersonnel(2002, 500)))));
      }
    }
    

    in.xml

    <container>
      <name>Hello</name>
      <numberOfEmployees year="2013">499.0</numberOfEmployees>
      <numberOfEmployees year="2012">550.0</numberOfEmployees>
    </container>
    

    Output

    Unmarshalling:
    Hello, [2013: 499.0, 2012: 550.0]
    Marshalling:
    <container>
      <name>World</name>
      <numberOfEmployees year="2001">1000.0</numberOfEmployees>
      <numberOfEmployees year="2002">500.0</numberOfEmployees>
    </container>