Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot deserialize multiple wrapped lists with conflicting local names #188

Closed
ecopoesis opened this issue Mar 23, 2016 · 4 comments
Closed
Labels
duplicate Issue is duplicate of another issue

Comments

@ecopoesis
Copy link

I'm trying to create a pair of immutable classes to handle serialization and deserialization of XML that looks like:

<?xml version="1.0" encoding="UTF-8"?>
<Outer xmlns="http://example.com">
  <Foo>outer foo</Foo>
  <Inners>
    <Inner>
      <Bar>inner 1 bar</Bar>
      <Baz>inner 2 baz</Baz>
    </Inner>
    <Inner>
      <Bar>inner 2 bar</Bar>
      <Baz>inner 2 baz</Baz>
    </Inner>
  </Inners>
</Outer>

I need to be able to serialize and deserialize both an Outer containing a list of Inners and an Inner by itself.

The test below works for serializing both objects, and deserializing Inner, but fails deserializing an Outer with the error com.fasterxml.jackson.databind.JsonMappingException: Duplicate property 'Inners' for [simple type, class BrokenTest$Outer]

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
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 org.testng.annotations.Test;

import java.util.Arrays;
import java.util.List;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;

public class BrokenTest
{
    private static final String NAMESPACE = "http://example.com";

    @JacksonXmlRootElement(localName="Outer", namespace = NAMESPACE)
    public static class Outer
    {
        @JacksonXmlProperty(localName="Foo", namespace = NAMESPACE)
        public final String foo;

        @JacksonXmlProperty(localName="Inner", namespace = NAMESPACE)
        @JacksonXmlElementWrapper(localName = "Inners", namespace = NAMESPACE)
        public final List<Inner> inners;

        @JsonCreator
        public Outer(
                @JacksonXmlProperty(localName="Foo", namespace = NAMESPACE) final String foo,
                @JacksonXmlProperty(localName="Inners", namespace = NAMESPACE) final List<Inner> inners)
        {
            this.foo = foo;
            this.inners = inners;
        }
    }

    @JacksonXmlRootElement(localName="Inner", namespace = NAMESPACE)
    public static class Inner
    {
        @JacksonXmlProperty(localName="Bar", namespace = NAMESPACE)
        public final String bar;

        @JacksonXmlProperty(localName="Baz", namespace = NAMESPACE)
        public final String baz;

        @JsonCreator
        public Inner(
                @JacksonXmlProperty(localName="Bar", namespace = NAMESPACE) final String bar,
                @JacksonXmlProperty(localName="Baz", namespace = NAMESPACE) final String baz)
        {
            this.bar = bar;
            this.baz = baz;
        }
    }

    @Test
    public void serializeInner() throws Exception
    {
        Inner inner = new Inner("inner bar", "inner baz");
        ObjectMapper mapper = new XmlMapper();
        String serialized = mapper.writeValueAsString(inner);
        assertEquals(serialized, "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>");
    }

    @Test
    public void deserializeInner() throws Exception
    {
        String serialized = "<Inner xmlns=\"http://example.com\"><Bar>inner bar</Bar><Baz>inner baz</Baz></Inner>";
        ObjectMapper mapper = new XmlMapper();
        Inner inner = mapper.readValue(serialized, Inner.class);
        assertNotNull(inner);
        assertEquals("inner bar", inner.bar);
        assertEquals("inner baz", inner.baz);
    }

    @Test
    public void serializeOuter() throws Exception
    {
        Outer outer = new Outer("outer foo", Arrays.asList(new Inner("inner 1 bar", "inner 1 baz"), new Inner("inner 2 bar", "inner 2 baz")));
        ObjectMapper mapper = new XmlMapper();
        String serialized = mapper.writeValueAsString(outer);
        assertEquals(serialized, "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>");
    }

    @Test
    public void deserializeOuter() throws Exception
    {
        String serialized = "<Outer xmlns=\"http://example.com\"><Foo>outer foo</Foo><Inners><Inner><Bar>inner 1 bar</Bar><Baz>inner 1 baz</Baz></Inner><Inner><Bar>inner 2 bar</Bar><Baz>inner 2 baz</Baz></Inner></Inners></Outer>";
        ObjectMapper mapper = new XmlMapper();
        Outer outer = mapper.readValue(serialized, Outer.class); // fails
        assertNotNull(outer);
        assertEquals("outer foo", outer.foo);
        assertEquals(2, outer.inners.size());
        assertEquals("inner 1 bar", outer.inners.get(0).bar);
        assertEquals("inner 1 baz", outer.inners.get(0).baz);
        assertEquals("inner 2 bar", outer.inners.get(1).bar);
        assertEquals("inner 2 baz", outer.inners.get(1).baz);
    }
}

If I change the @JacksonXmlProperty annotation on Outer's constructor to use the localName "Inner" instead of "Inners", I get the exception com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Inners" (class BrokenTest$Outer), not marked as ignorable (2 known properties: "Foo", "Inner"]).

This is using version 2.7.3

@ecopoesis
Copy link
Author

I've found a workaround that makes these tests pass. If the @JacksonXmlProperty for the inners parameter has it localName changed to "XXX" (or anything that's not part of XML), everything works.

@cowtowncoder
Copy link
Member

Yes, unfortunately handling of wrappers/wrapped is implemented in such a way that currently naming collisions occur even if outer wrapper names differ. I hope this can be fixed in future.

@groothuyse
Copy link

It's 2020 and this bug is still causing pain :(

@cowtowncoder cowtowncoder changed the title can't deserialize a wrapped list using property and constructor annotations Cannot deserialize multiple wrapped lists with conflicting local names May 20, 2020
@cowtowncoder
Copy link
Member

I think this is same as #192, closing as dup (since that one has more discussion).

@cowtowncoder cowtowncoder added the duplicate Issue is duplicate of another issue label May 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate Issue is duplicate of another issue
Projects
None yet
Development

No branches or pull requests

3 participants