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

MismatchedInputException when reading field with same name as class #433

Closed
petervandenbroek opened this issue Nov 13, 2020 · 6 comments
Closed

Comments

@petervandenbroek
Copy link

petervandenbroek commented Nov 13, 2020

Ticket #393 has been fixed in that a Jackson annotated class can be read if a nested field with the same name as the class is found.
However, when the class is generated from an XSD and thus has JAXB annotations, this still seems to fail.

For example, this XML should now be read correctly:

<Product>
    <Price>
        <Start>50</Start>
        <End>99</End>
        <Price>2.53</Price>
    </Price>
</Product>

We've created an XSD based on this and generated models from it.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="Product">
        <xs:complexType>
            <xs:all>
                <!-- many other fields -->
                <xs:element name="Prices" type="Prices" minOccurs="0" />
                <!-- many other fields -->
            </xs:all>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="Prices">
        <xs:sequence>
            <xs:element name="Price" type="Price" minOccurs="0" maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="Price">
        <xs:sequence>
            <xs:element name="Start" type="xs:int" minOccurs="0" />
            <xs:element name="End" type="xs:int" minOccurs="0" />
            <xs:element name="Price" type="xs:decimal" minOccurs="0" />
        </xs:sequence>
    </xs:complexType>

</xs:schema>

The generated classes look like this:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Product", propOrder = {

})
public class Product {

    @XmlElement(name = "Prices")
    protected Prices prices;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Prices", propOrder = {
    "price"
})
public class Prices {

    @XmlElement(name = "Price")
    protected List<Price> price;

    public List<Price> getPrice() {
        if (price == null) {
            price = new ArrayList<Price>();
        }
        return this.price;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Price", propOrder = {
    "start",
    "end",
    "price"
})
@Generated(value = "com.sun.tools.xjc.Driver", date = "2020-11-12T02:11:49+01:00", comments = "JAXB RI v2.3.2")
public class Price {

    @XmlElement(name = "Start")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2020-11-13T08:32:44+01:00", comments = "JAXB RI v2.3.2")
    protected Integer start;
    @XmlElement(name = "End")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2020-11-13T08:32:44+01:00", comments = "JAXB RI v2.3.2")
    protected Integer end;
    @XmlElement(name = "Price")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2020-11-13T08:32:44+01:00", comments = "JAXB RI v2.3.2")
    protected BigDecimal price;

    public Integer getStart() {
        return start;
    }
    public void setStart(Integer value) {
        this.start = value;
    }
    public Integer getEnd() {
        return end;
    }
    public void setEnd(Integer value) {
        this.end = value;
    }
    public BigDecimal getPrice() {
        return price;
    }
    public void setPrice(BigDecimal value) {
        this.price = value;
    }
}

(for readability and relevance, I've removed the "generated" annotation and unrelated comments.)

Lastly, this is how the XmlMapper is created:

private static ObjectMapper createObjectMapper() {
    return XmlMapper.xmlBuilder()
            .defaultUseWrapper(false)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
            .configure(SerializationFeature.INDENT_OUTPUT, true)
            .build()
            .registerModule(new JaxbAnnotationModule());
}

When creating a simple test that creates an object to write and read again, it fails during the reading.

The test:

    @Test
    void writeAndReadProduct() throws Exception {
        ObjectFactory factory = new ObjectFactory();
        Product product = factory.createProduct();
        Prices prices = factory.createPrices();
        Price price = factory.createPrice();
        price.setStart(50);
        price.setEnd(99);
        price.setPrice(new BigDecimal("2.53"));
        prices.getPrice().add(price);
        product.setPrices(prices);

        ObjectMapper mapper = createObjectMapper();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        mapper.writeValue(outputStream, product);

        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        Assertions.assertThat(mapper.readValue(inputStream, Product.class))
                .usingRecursiveComparison()
                .isEqualTo(product);
    }

The exception: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.math.BigDecimal out of START_OBJECT token at [Source: (ByteArrayInputStream); line: 15, column: 7] (through reference chain: package.Product["Prices"]->package.Prices["Price"]->java.util.ArrayList[0]->package.Price["Price"])

There is a way around this problem and that is to move the "price" field as first element of the "price" object.
But as you might expect, I have no influence on the input data here..

@cowtowncoder cowtowncoder changed the title MismatchedInputException when reading field with same name as class MismatchedInputException when reading field with same name as class Nov 13, 2020
@cowtowncoder
Copy link
Member

One quick note: for tests, one should not use .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) -- that often suppresses critical information on mismatches. I know many users enable it for production but it's a bane of my debugging, constantly hiding messages that would have helper users.
I'll try to see if uncommenting this gives more information.

cowtowncoder added a commit that referenced this issue Nov 13, 2020
@cowtowncoder
Copy link
Member

Yes, I can reproduce this issue, and using just Jackson annotations as well (so unlikely to be issue wrt JAXB annotation mapping.
Adding failing test, see if I can figure out how to fix.

@cowtowncoder cowtowncoder added the has-failing-test Indicates that there exists a test case (under `failing/`) to reproduce the issue label Nov 13, 2020
@cowtowncoder
Copy link
Member

Wait, no: test I had was wrong and after fixing mismatch with name: first XML sample is wrong and XML must be:

<Product>
 <Prices>
  <Price>
   <Start>50</Start>
   <Price>2.53</Price>
   <End>99</End>
  </Price>
 </Prices>
</Product>

and that does work with 2.12.0-rc2.

@cowtowncoder cowtowncoder added test-needed and removed has-failing-test Indicates that there exists a test case (under `failing/`) to reproduce the issue labels Nov 16, 2020
cowtowncoder added a commit that referenced this issue Nov 16, 2020
@cowtowncoder cowtowncoder added this to the 2.12.0-rc2 milestone Nov 16, 2020
@petervandenbroek
Copy link
Author

@cowtowncoder, thanks for your quick support.
However, I'm not sure why this issue is closed, have you ran my example test with JAXB annotations?

The Price field must be the last element of the Price object (according the XSD), not the second as you've mentioned in your comment.

Side note, I know using the "fail on unknown properties" is a bad practice, we use it to skip a lot of fields in the source data we receive. I should have left it out of my example, but it doesn't really change the failing outcome.

@cowtowncoder
Copy link
Member

@petervandenbroek ah. Changing of ordering was accidental: I think I was playing with order during testing and had forgotten to revert it back. Thank you for pointing it out.

At this point (2.12.0-rc2) ordering does not seem to cause issues any more -- this makes sense as the handling of "wrapping" (or lack thereof) was rewritten for 2.12 to work reliably.
That is why I closed the issue based on simplified example: I do not think use of JAXB annotations was problematic and root cause seems likelier to be more general issue with end tag matching in 2.11 and prir versions.

About the only thing I can see that is different (and that I missed) is used of DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS -- I'll see if adding that to test makes any difference.

If you can still reproduce this with 2.12.0-rc2 feel free to reopen.

@cowtowncoder
Copy link
Member

DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS does not fail deserialization; serializing results seems to produce expected XML output as well.

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

No branches or pull requests

2 participants