Skip to content

Commit fe1878b

Browse files
authored
Fixes #714: no more trailing tokens after root null (#752)
1 parent b1ac58b commit fe1878b

File tree

4 files changed

+30
-16
lines changed

4 files changed

+30
-16
lines changed

release-notes/VERSION-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Project: jackson-dataformat-xml
66

77
#508: `XmlMapper` is unable to deserialise into an empty record
88
(reported by @protazy)
9+
#714: Root-level `null` handling (via `xsi:nil`) leaves trailing token in
10+
`JsonParser`-exposed token stream
911
#745: Add feature to include `standalone='yes'` in xml declaration
1012
(contributed by @duoduobingbing)
1113

src/main/java/com/fasterxml/jackson/dataformat/xml/deser/FromXmlParser.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private Feature(boolean defaultState) {
253253
*/
254254

255255
/**
256-
* Bitfield that indicates which numeric representations
256+
* Bit field that indicates which numeric representations
257257
* have been calculated for the current type
258258
*/
259259
protected int _numTypesValid = NR_UNKNOWN;
@@ -297,6 +297,8 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures,
297297
// changed in 2.10.2
298298
if (_xmlTokens.hasXsiNil()) {
299299
_nextToken = JsonToken.VALUE_NULL;
300+
// 21-Apr-2025, tatu: [dataformat-xml#714] Must "flush" the stream
301+
_xmlTokens.markAsStreamEnd();
300302
} else {
301303
switch (firstToken) {
302304
case XmlTokenStream.XML_START_ELEMENT:
@@ -689,16 +691,16 @@ public JsonToken nextToken() throws IOException
689691
{
690692
JsonToken t = nextToken0();
691693
if (t != null) {
692-
final String loc = (_parsingContext == null) ? "NULL" : String.valueOf(_parsingContext.pathAsPointer());
694+
final String loc = (_parsingContext == null) ? "<null>" : "'"+String.valueOf(_parsingContext.pathAsPointer())+"'";
693695
switch (t) {
694696
case FIELD_NAME:
695-
System.out.printf("FromXmlParser.nextToken() at '%s': JsonToken.FIELD_NAME '%s'\n", loc, _parsingContext.currentName());
697+
System.out.printf("FromXmlParser.nextToken() at %s: JsonToken.FIELD_NAME '%s'\n", loc, _parsingContext.getCurrentName());
696698
break;
697699
case VALUE_STRING:
698-
System.out.printf("FromXmlParser.nextToken() at '%s': JsonToken.VALUE_STRING '%s'\n", loc, getText());
700+
System.out.printf("FromXmlParser.nextToken() at %s: JsonToken.VALUE_STRING '%s'\n", loc, getText());
699701
break;
700702
default:
701-
System.out.printf("FromXmlParser.nextToken() at '%s': %s\n", loc, t);
703+
System.out.printf("FromXmlParser.nextToken() at %s: %s\n", loc, t);
702704
}
703705
}
704706
return t;

src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,6 @@ public int next() throws XMLStreamException
269269
case XML_START_ELEMENT:
270270
System.out.printf(" XmlTokenStream.next(): XML_START_ELEMENT '%s' %s\n", _localName, _loc());
271271
break;
272-
case XML_DELAYED_START_ELEMENT:
273-
System.out.printf(" XmlTokenStream.next(): XML_DELAYED_START_ELEMENT '%s' %s\n", _localName, _loc());
274-
break;
275272
case XML_END_ELEMENT:
276273
// 24-May-2020, tatu: no name available for end element so do not print
277274
System.out.printf(" XmlTokenStream.next(): XML_END_ELEMENT %s\n", _loc());
@@ -406,6 +403,16 @@ protected void pushbackCurrentToken()
406403
_repeatCurrentToken = true;
407404
}
408405

406+
/**
407+
* Method that can be called to mark stream as having reached end of stream.
408+
*
409+
* @since 2.19
410+
*/
411+
protected void markAsStreamEnd()
412+
{
413+
_currentState = XML_END;
414+
}
415+
409416
/**
410417
* Method called to skip any attributes current START_ELEMENT may have,
411418
* so that they are not returned as token.
@@ -460,6 +467,7 @@ private final int _next() throws XMLStreamException
460467
// 08-Jul-2021, tatu: as per [dataformat-xml#467] just skip anything
461468
// element might have, no need to ensure it was empty
462469
_xmlReader.skipElement();
470+
//System.out.println(" XmlTokenStream._next(): Got xsi:nil, skipping element");
463471
return _handleEndElement();
464472
}
465473
if (_nextAttributeIndex < _attributeCount) {
@@ -693,7 +701,7 @@ private final void _checkXsiAttributes() {
693701
int count = _xmlReader.getAttributeCount();
694702
_attributeCount = count;
695703

696-
// [dataformat-xml#354]: xsi:nul handling; at first only if first attribute
704+
// [dataformat-xml#354]: xsi:nil handling; at first only if first attribute
697705
if (count >= 1) {
698706
// [dataformat-xml#468]: may disable xsi:nil processing
699707
if (_cfgProcessXsiNil
@@ -703,6 +711,7 @@ private final void _checkXsiAttributes() {
703711
_nextAttributeIndex = 1;
704712
// but only mark as nil marker if enabled
705713
_xsiNilFound = "true".equals(_xmlReader.getAttributeValue(0));
714+
//System.out.println(" XMLTokenStream._checkXsiAttributes(), _xsiNilFound: "+_xsiNilFound);
706715
return;
707716
}
708717
}
@@ -813,6 +822,8 @@ private final int _handleEndElement()
813822

814823
}
815824
} else {
825+
//System.out.println(" XMLTokenStream._handleEndElement(): no wrapper");
826+
816827
// Not (necessarily) known, as per above, so:
817828
_localName = "";
818829
_namespaceURI = "";

src/test/java/com/fasterxml/jackson/dataformat/xml/tofix/XsiNilBasic714Test.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
99
import com.fasterxml.jackson.dataformat.xml.XmlTestUtil;
1010
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
11-
import com.fasterxml.jackson.dataformat.xml.testutil.failure.JacksonTestFailureExpected;
1211

1312
import static org.junit.jupiter.api.Assertions.*;
1413

@@ -22,7 +21,6 @@ public class XsiNilBasic714Test extends XmlTestUtil
2221
.build();
2322

2423
// [dataformat-xml#714]: trailing END_OBJECT
25-
@JacksonTestFailureExpected
2624
@Test
2725
public void testRootPojoAsNull() throws Exception
2826
{
@@ -35,7 +33,6 @@ public void testRootPojoAsNull() throws Exception
3533
// [dataformat-xml#468]: Allow disabling xsi:nil special handling
3634

3735
// [dataformat-xml#714]: trailing END_OBJECT
38-
@JacksonTestFailureExpected
3936
@Test
4037
public void testDisableXsiNilRootProcessing() throws Exception
4138
{
@@ -46,10 +43,12 @@ public void testDisableXsiNilRootProcessing() throws Exception
4643
assertEquals("null", r.readValue(DOC).toString());
4744

4845
// 07-Jul-2021, tatu: Alas! 2.x sets format feature flags too late to
49-
// affect root element (3.0 works correctly). So cannot test
50-
51-
ObjectReader noXsiNilReader = r.without(FromXmlParser.Feature.PROCESS_XSI_NIL);
46+
// affect root element (3.0 works correctly). Need a new mapper
47+
XmlMapper mapper2 = mapperBuilder()
48+
.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
49+
.disable(FromXmlParser.Feature.PROCESS_XSI_NIL)
50+
.build();
5251
assertEquals(a2q("{'nil':'true'}"),
53-
noXsiNilReader.readValue(DOC).toString());
52+
mapper2.readerFor(JsonNode.class).readValue(DOC).toString());
5453
}
5554
}

0 commit comments

Comments
 (0)