Home > OS >  Copying List in Setter makes XML Unmarshaller not work
Copying List in Setter makes XML Unmarshaller not work

Time:02-07

I have a DataBean like this:

@XmlRootElement
public class DataBean {
    private List<String> data;

    @XmlElement(name = "data")
    public List<String> getData() {
        return data != null ? Collections.unmodifiableList(data) : null;
    }

    public void setData(List<String> data) {
        data = new ArrayList<>(data);
        data.sort(Comparator.naturalOrder());
        this.data = data;
    }
}

Inside of the DataBean, the list must be sorted, otherwise the DataBean is not usable in my domain. In order to avoid side effects, I copy the list in the setter and then sort it.

Marshalling this bean works fine, but when unmarshalling, the data list is empty (not null). Here is some code to demonstrate:

public class XmlTester {

    public static void main(String[] args) {
        List<String> data = Arrays.asList("one", "two", "three", "four");
        DataBean dataBean = new DataBean();
        dataBean.setData(data);
        System.out.printf("Data in original bean: %s%n%n", dataBean.getData().toString());

        try {
            String xmlString = serialize(dataBean);
            System.out.printf("XML:%n%s%n", xmlString);
            DataBean deserializedDataBean = deserialize(xmlString);
            System.out.printf("Data in deserialized bean: %s%n%n", deserializedDataBean.getData().toString());
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }

    private static String serialize(DataBean dataBean) throws JAXBException {
        StringWriter w = new StringWriter();
        JAXBContext jc = JAXBContext.newInstance(DataBean.class);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(dataBean, w);
        return w.toString();
    }

    private static DataBean deserialize(String xmlString) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(DataBean.class);
        Unmarshaller um = context.createUnmarshaller();
        return (DataBean) um.unmarshal(new StringReader(xmlString));
    }
}

This produces the following output:

Data in original bean: [four, one, three, two]

XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<dataBean>
    <data>four</data>
    <data>one</data>
    <data>three</data>
    <data>two</data>
</dataBean>

Data in deserialized bean: []

If I remove the line data = new ArrayList<>(data); from the setter in the bean, unmarshalling works like a charm, so the copy operation seems to be the problem.

Why does copying make the list in the deserialized bean empty, and can I fix this without removing the copy operation?

Of course I could instead test if the data is sorted and throw an IllegalArgumentException if it isn't, but I would prefer to sort a copy in the setter.

CodePudding user response:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class DataBean {


    @XmlElement(name = "data")
    private List<String> data;

    public List<String> getData() {
        return data != null ? Collections.unmodifiableList(data) : null;
    }

    public void setData(List<String> data) {
        data = new ArrayList<>(data);
        data.sort(Comparator.naturalOrder());
        this.data = data;
    }
}
Data in original bean: [four, one, three, two]

XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<dataBean>
    <data>four</data>
    <data>one</data>
    <data>three</data>
    <data>two</data>
</dataBean>

Data in deserialized bean: [four, one, three, two]
  •  Tags:  
  • Related