jackson-dataformat-xml icon indicating copy to clipboard operation
jackson-dataformat-xml copied to clipboard

Collection values overwritten if item elements are not contiguous (mixed content)

Open vchimishuk opened this issue 6 years ago • 5 comments

Looks like we have a bug in the parser. Parsing next simple document leads to data loss.

<Data>
    <foo>foo1</foo>
    <foo>foo2</foo>
    <bar>bar1</bar>
    <foo>foo3</foo>
    <foo>foo4</foo>
</Data>

Expected result: foo java-property contains list of 4 values. Actual result: foo java-property contains list of 2.

Setter for foo property is called two times for every tags group: foo1, foo2 and foo3, foo4. Second setter call overrides data gathered by the first call. As result parsing result contains foo list as foo3, foo4 instead of expected foo1, foo2, foo3, foo4. Here is a java-code to reproduce the issue.

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.util.List;

@JacksonXmlRootElement(localName = "data")
class Data {
    @JacksonXmlCData
    @JacksonXmlElementWrapper(useWrapping = false)
    @JacksonXmlProperty
    private List<String> foo;

    @JacksonXmlCData
    @JacksonXmlElementWrapper(useWrapping = false)
    @JacksonXmlProperty
    private List<String> bar;

    public List<String> getFoo() {
        return foo;
    }

    public void setFoo(List<String> foo) {
        this.foo = foo;
    }

    public List<String> getBar() {
        return bar;
    }

    public void setBar(List<String> bar) {
        this.bar = bar;
    }
}

public class Foo {
    public static void main(String[] args) throws JsonProcessingException {
        String xml = ""
                + "<Data>"
                + "    <foo>foo1</foo>"
                + "    <foo>foo2</foo>"
                + "    <bar>bar1</bar>"
                + "    <foo>foo3</foo>"
                + "    <foo>foo4</foo>"
                + "</Data>";

        XmlMapper m = new XmlMapper();
        Data data = m.readValue(xml, Data.class);

        System.err.println(data.getFoo()); // Expected ["foo1", "foo2", "foo3", "foo4"] but ["foo3", "foo4"] given.
    }
}

jackson-dataformat-xml version: 2.10.0

vchimishuk avatar Oct 05 '19 20:10 vchimishuk

Yes. This is a known problem, and due to difficulty in changing handling it is unlikely to be improved upon any time soon.

cowtowncoder avatar Oct 05 '19 23:10 cowtowncoder

I see. Thank you for the reply.

vchimishuk avatar Oct 07 '19 07:10 vchimishuk

Actually... you may be able to work around this issue by overriding setters (set methods) to add values into existing list, instead of replace, and initializing fields into empty Lists (or have setter check to initialize if null).

In fact there is even another method, available with 2.9 and later: see "@JsonSetter" explanation on https://medium.com/@cowtowncoder/jackson-2-9-features-b2a19029e9ff -- using which you can get "merge" functionality that also should work around the issue.

cowtowncoder avatar Oct 23 '19 16:10 cowtowncoder

This is the way I use to solve the issue. Thank you.

vchimishuk avatar Oct 23 '19 17:10 vchimishuk

Ok good. Maybe this helps others to find it too.

cowtowncoder avatar Oct 23 '19 18:10 cowtowncoder