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

Invalid index error when deserializing unwrapped list element with multiple attributes #101

Closed
mrserverless opened this issue Feb 11, 2014 · 11 comments
Milestone

Comments

@mrserverless
Copy link

This issue was originally pointed out by https:/ShijunK in #64

I also came across this issue while working with a very complex XML and thought it would be worthwhile raising this on its own. I've re-used ShijunK's example:

public class UnwrappedTest {
    @JacksonXmlRootElement(localName = "root")    
    static class Root {

        @JacksonXmlProperty(localName = "unwrapped")
        @JacksonXmlElementWrapper(useWrapping = false)
        public List<UnwrappedElement> unwrapped;

        @JacksonXmlProperty(localName = "name")
        public String name;

        public static class UnwrappedElement {
            public UnwrappedElement () {}

            public UnwrappedElement (String id, String type) {
                this.id = id;
                this.type = type;
            }

            @JacksonXmlProperty(isAttribute = true)
            public String id;

            @JacksonXmlProperty(isAttribute = true)
            public String type;
        }
    }

    protected final static XmlMapper xmlMapper = new XmlMapper();
    protected final String rootXml = "<root>"  + System.lineSeparator() +
            "  <unwrapped id=\"1\" type=\"string\"/>"  + System.lineSeparator() +
            "  <unwrapped id=\"2\" type=\"string\"/>"  + System.lineSeparator() +
            "  <name>text</name>"  + System.lineSeparator() +
            "</root>";

    protected final Root rootObject = new Root();

    @Before
    public void setUp() {
        xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
        rootObject.unwrapped = Arrays.asList(
                new Root.UnwrappedElement("1", "string"),
                new Root.UnwrappedElement("2", "string")
        );
        rootObject.name = "text";
    }

    @Test
    public void serializeTest () throws Exception {
        //SUCCESS
        assertThat(xmlMapper.writeValueAsString(rootObject)).isEqualTo(rootXml);
    }

    @Test
    public void derializeTest () throws Exception {
        //FAILS with invalid index error
        assertThat(xmlMapper.readValue(rootXml, Root.class)).isEqualsToByComparingFields(rootObject);
    }
}

The exception is:

java.lang.IllegalArgumentException: Invalid index 0; current element has only 0 attributes
at com.ctc.wstx.sr.AttributeCollector.throwIndex(AttributeCollector.java:1037)
at com.ctc.wstx.sr.AttributeCollector.getLocalName(AttributeCollector.java:330)
at com.ctc.wstx.sr.BasicStreamReader.getAttributeLocalName(BasicStreamReader.java:583)
at com.fasterxml.jackson.dataformat.xml.deser.XmlTokenStream._next(XmlTokenStream.java:306)
at com.fasterxml.jackson.dataformat.xml.deser.XmlTokenStream.next(XmlTokenStream.java:162)
at com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser.nextToken(FromXmlParser.java:451)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:238)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.dataformat.xml.deser.WrapperHandlingDeserializer.deserialize(WrapperHandlingDeserializer.java:109)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2993)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2098)
at com.mgl.caf.loaniq.api.models.UnwrappedTest.testUnwrappedWithOptionalAttribute(UnwrappedTest.java:44)

I'm using Jackson 2.3.1 with Woodstox 4.2.0. I will attempt to work around this issue by using JAXB in the meanwhile.

@mrserverless
Copy link
Author

updated test case to add in serializeTest also, which works.

@cowtowncoder
Copy link
Member

Thank you for reporting this.

@mrserverless
Copy link
Author

I'm also finding that when I set my POJO to use all JAXB annotations:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "root")
static class Root {

    @XmlElement(name="unwrapped")
    public List<UnwrappedElement> unwrapped;

    public static class UnwrappedElement {

        public UnwrappedElement (String id, String type) {
            this.id = id;
            this.type = type;
        }

        @XmlAttribute(name = "id")
        public String id;

        @XmlAttribute(name = "type")
        public String type;
    }
}

And run the above tests, I get a very strange result:

<Root><unwrapped>
    <unwrapped>
      <id>1</id>
      <type>string</type>
    </unwrapped>
    <unwrapped>
      <id>2</id>
      <type>string</type>
    </unwrapped>
  </unwrapped>
</Root>

Instead of the expected:

<root>
    <unwrapped id="1" type="string"/>
    <unwrapped id="2" type="string"/>
</root>

It appears as if my JAXB annotations are completely being ignored. Reading #6 and http://blog.bdoughan.com/2012/12/jaxb-representing-null-and-empty.html I figured the default behavior for Lists should be "unwrapped" for JAXB annotations.

I've also tried using Jackson annotations with:

jacksonXmlModule.setDefaultUseWrapper(false);

But that would give me the original invalid index error.

mrserverless added a commit to mrserverless/dropwizard-xml that referenced this issue Feb 12, 2014
@cowtowncoder
Copy link
Member

On part of defaulting: Jackson does not necessarily use JAXB defaults -- it is not a JAXB implementation; it can use JAXB annotations as input, but otherwise there are some discrepancies.
So default for Jackson XML is "wrapped", which is different from JAXB defaults.

As to JAXB annotations being ignored: have you enabled support for JAXB annotations? XML module does not use them automatically (similar to JSON support).

@cowtowncoder
Copy link
Member

Ok I can reproduce this issue with given test. Thanks!

cowtowncoder added a commit that referenced this issue Feb 14, 2014
@mrserverless
Copy link
Author

Thanks cowtowncoder,

I tried debugging this and I can see where the issue is. After jackson finished looping through the valid attributes in the List elements, it continue to call getAttributeLocalName() on the non-List element "name" which has a attributeCount of 0.

If I place the non-List element before the List element, then this problem is solved. But with large amounts of auto-generated POJOs sometime it's not so easy to control the ordering :)

@cowtowncoder
Copy link
Member

Yes, the problem is related to attributeCount not being cleared to match actual number of attributes. That much I could see by bit of debugging. The question then is where is this assignment missing...

@escholz
Copy link

escholz commented Apr 2, 2014

I came across this issue as well and have a one line fix if you happen to be building from source rather than an archive.

When XmlTokenStream#_initStartElement() is called on an element that is not part of the current collection it assumes that the collection has ended implicitly. When this happens it returns the XML_END_ELEMENT token id and doesn't pull the new attribute count from the _xmlReader.

Move _attributeCount = _xmlReader.getAttributeCount(); up under final String localName = _xmlReader.getLocalName(); in the XmlTokenStream#_initStartElement() method.

There hasn't been much traction on this issue in a couple months, so I wasn't certain if it had already been fixed internally. If you'd like me to submit a pull request I'd be happy to.

@cowtowncoder
Copy link
Member

Thanks! I can check this out locally and merge if all unit tests pass.

cowtowncoder added a commit that referenced this issue Apr 3, 2014
@cowtowncoder
Copy link
Member

Excellent, thank you @escholz! I fixed this in master and 2.3 (for 2.3.3 release); all tests pass, including ones that were failing for this one earlier.

@cowtowncoder cowtowncoder added this to the 2.3.3 milestone Apr 3, 2014
@escholz
Copy link

escholz commented Apr 3, 2014

Great! Glad I could help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants