diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 98a96b2f..2ac9e556 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,6 +6,8 @@ Project: jackson-dataformat-xml #508: `XmlMapper` is unable to deserialise into an empty record (reported by @protazy) +#714: Root-level `null` handling (via `xsi:nil`) leaves trailing token in + `JsonParser`-exposed token stream #745: Add feature to include `standalone='yes'` in xml declaration (contributed by @duoduobingbing) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java index 089f1066..7e723bdc 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java @@ -253,7 +253,7 @@ private Feature(boolean defaultState) { */ /** - * Bitfield that indicates which numeric representations + * Bit field that indicates which numeric representations * have been calculated for the current type */ protected int _numTypesValid = NR_UNKNOWN; @@ -297,6 +297,8 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures, // changed in 2.10.2 if (_xmlTokens.hasXsiNil()) { _nextToken = JsonToken.VALUE_NULL; + // 21-Apr-2025, tatu: [dataformat-xml#714] Must "flush" the stream + _xmlTokens.markAsStreamEnd(); } else { switch (firstToken) { case XmlTokenStream.XML_START_ELEMENT: @@ -689,16 +691,16 @@ public JsonToken nextToken() throws IOException { JsonToken t = nextToken0(); if (t != null) { - final String loc = (_parsingContext == null) ? "NULL" : String.valueOf(_parsingContext.pathAsPointer()); + final String loc = (_parsingContext == null) ? "" : "'"+String.valueOf(_parsingContext.pathAsPointer())+"'"; switch (t) { case FIELD_NAME: - System.out.printf("FromXmlParser.nextToken() at '%s': JsonToken.FIELD_NAME '%s'\n", loc, _parsingContext.currentName()); + System.out.printf("FromXmlParser.nextToken() at %s: JsonToken.FIELD_NAME '%s'\n", loc, _parsingContext.getCurrentName()); break; case VALUE_STRING: - System.out.printf("FromXmlParser.nextToken() at '%s': JsonToken.VALUE_STRING '%s'\n", loc, getText()); + System.out.printf("FromXmlParser.nextToken() at %s: JsonToken.VALUE_STRING '%s'\n", loc, getText()); break; default: - System.out.printf("FromXmlParser.nextToken() at '%s': %s\n", loc, t); + System.out.printf("FromXmlParser.nextToken() at %s: %s\n", loc, t); } } return t; diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java index 8e132016..2a7c78be 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java @@ -269,9 +269,6 @@ public int next() throws XMLStreamException case XML_START_ELEMENT: System.out.printf(" XmlTokenStream.next(): XML_START_ELEMENT '%s' %s\n", _localName, _loc()); break; - case XML_DELAYED_START_ELEMENT: - System.out.printf(" XmlTokenStream.next(): XML_DELAYED_START_ELEMENT '%s' %s\n", _localName, _loc()); - break; case XML_END_ELEMENT: // 24-May-2020, tatu: no name available for end element so do not print System.out.printf(" XmlTokenStream.next(): XML_END_ELEMENT %s\n", _loc()); @@ -406,6 +403,16 @@ protected void pushbackCurrentToken() _repeatCurrentToken = true; } + /** + * Method that can be called to mark stream as having reached end of stream. + * + * @since 2.19 + */ + protected void markAsStreamEnd() + { + _currentState = XML_END; + } + /** * Method called to skip any attributes current START_ELEMENT may have, * so that they are not returned as token. @@ -460,6 +467,7 @@ private final int _next() throws XMLStreamException // 08-Jul-2021, tatu: as per [dataformat-xml#467] just skip anything // element might have, no need to ensure it was empty _xmlReader.skipElement(); +//System.out.println(" XmlTokenStream._next(): Got xsi:nil, skipping element"); return _handleEndElement(); } if (_nextAttributeIndex < _attributeCount) { @@ -693,7 +701,7 @@ private final void _checkXsiAttributes() { int count = _xmlReader.getAttributeCount(); _attributeCount = count; - // [dataformat-xml#354]: xsi:nul handling; at first only if first attribute + // [dataformat-xml#354]: xsi:nil handling; at first only if first attribute if (count >= 1) { // [dataformat-xml#468]: may disable xsi:nil processing if (_cfgProcessXsiNil @@ -703,6 +711,7 @@ private final void _checkXsiAttributes() { _nextAttributeIndex = 1; // but only mark as nil marker if enabled _xsiNilFound = "true".equals(_xmlReader.getAttributeValue(0)); +//System.out.println(" XMLTokenStream._checkXsiAttributes(), _xsiNilFound: "+_xsiNilFound); return; } } @@ -813,6 +822,8 @@ private final int _handleEndElement() } } else { +//System.out.println(" XMLTokenStream._handleEndElement(): no wrapper"); + // Not (necessarily) known, as per above, so: _localName = ""; _namespaceURI = ""; diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/tofix/XsiNilBasic714Test.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/tofix/XsiNilBasic714Test.java index d7ac244a..b708dabd 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/tofix/XsiNilBasic714Test.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/tofix/XsiNilBasic714Test.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.XmlTestUtil; import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser; -import com.fasterxml.jackson.dataformat.xml.testutil.failure.JacksonTestFailureExpected; import static org.junit.jupiter.api.Assertions.*; @@ -22,7 +21,6 @@ public class XsiNilBasic714Test extends XmlTestUtil .build(); // [dataformat-xml#714]: trailing END_OBJECT - @JacksonTestFailureExpected @Test public void testRootPojoAsNull() throws Exception { @@ -35,7 +33,6 @@ public void testRootPojoAsNull() throws Exception // [dataformat-xml#468]: Allow disabling xsi:nil special handling // [dataformat-xml#714]: trailing END_OBJECT - @JacksonTestFailureExpected @Test public void testDisableXsiNilRootProcessing() throws Exception { @@ -46,10 +43,12 @@ public void testDisableXsiNilRootProcessing() throws Exception assertEquals("null", r.readValue(DOC).toString()); // 07-Jul-2021, tatu: Alas! 2.x sets format feature flags too late to - // affect root element (3.0 works correctly). So cannot test - - ObjectReader noXsiNilReader = r.without(FromXmlParser.Feature.PROCESS_XSI_NIL); + // affect root element (3.0 works correctly). Need a new mapper + XmlMapper mapper2 = mapperBuilder() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) + .disable(FromXmlParser.Feature.PROCESS_XSI_NIL) + .build(); assertEquals(a2q("{'nil':'true'}"), - noXsiNilReader.readValue(DOC).toString()); + mapper2.readerFor(JsonNode.class).readValue(DOC).toString()); } }