Search code examples
javaxml-parsingxstream

Parsing with XStream - empty tags and collections


I am using XStream to convert XML into domain objects, and have come by a problem. Omitting a few details, the XML looks like this :

<airport>
    <flights>
        <flight>.....</flight>
        <flight>.....</flight>
        <flight>.....</flight>
    </flights>
</airport>

There can be 0 to N flight elements. The flight elements themselves contain other elements. I have created classes for airport, flights, and flight and registered them with the xstream.alias function.

xstream = new XStream();
xstream.alias("airport", AirportPojo.class);
xstream.alias("flights", FlightsPojo.class);
xstream.alias("flight", FlightPojo.class);
xstream.useAttributeFor(AirportPojo.class, "flights");
xstream.addImplicitCollection(FlightsPojo.class, "flights", FlightPojo.class);
AirportPojo airportPojo = (AirportPojo) xstream.fromXML(xml);

So, after converting, this gives me an AirportPojo object containing a FlightsPojo object, containing a collection of FlightPojo objects. However, when there are 0 flight elements it seems that the collection of FlightPojos is null. I would expect (and prefer) the list to be initialized but with zero elements in it. How could I accomplish this? Bear in mind that I cannot use annotations as this is a legacy project.


Solution

  • How about implementing a custom converter?

    class FlightsConverter implements Converter {
        @Override
        public boolean canConvert(Class clazz) {
            return clazz.equals(FlightsPojo.class);
        }
    
        @Override
        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
            FlightsPojo flightsPojo = new FlightsPojo();
            flightsPojo.setFlights(new ArrayList<FlightPojo>());
            while (reader.hasMoreChildren()) {
                reader.moveDown();
                FlightPojo flightPojo = (FlightPojo) context.convertAnother(flightsPojo, FlightPojo.class);
                flightsPojo.getFlights().add(flightPojo);
                System.out.println(reader.getValue());
                reader.moveUp();
            }
            return flightsPojo;
        }
    
        @Override
        public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
            // todo...
        }
    }
    

    And hook it in like so:

        XStream xstream = new XStream();
        xstream.registerConverter(new FlightsConverter());
        xstream.alias("airport", AirportPojo.class);
        xstream.alias("flights", FlightsPojo.class);
        xstream.alias("flight", FlightPojo.class);
        xstream.useAttributeFor(AirportPojo.class, "flights");
        AirportPojo airportPojo = (AirportPojo) xstream.fromXML(xml);
    

    Hope this helps ;)