From e3229bb1cd6079f789c5bf66c84eb52bd6df1b39 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 21 Aug 2026 16:50:12 -0700 Subject: [PATCH] Return null from nextTextValue() at end of input (#899) Port of the 3.x fix (PR #899 by @Sahana2524) to the 2.x line. FromXmlParser.nextTextValue() runs the XmlTokenStream event through a switch in which the XML_END branch has no `break`, so it falls into `default:` and throws IllegalStateException. One call past the last token therefore raises an unchecked exception instead of reporting end-of-input as null, the way nextToken() already does for XML_END; callers that only catch JsonProcessingException do not see it. Adds the missing `break` so XML_END reaches the method's shared `return null`, plus a test in XmlParserNextXxxTest covering one call past the end. Co-Authored-By: Claude Opus 5 (1M context) --- release-notes/CREDITS-2.x | 3 +++ release-notes/VERSION-2.x | 4 +++- .../dataformat/xml/deser/FromXmlParser.java | 1 + .../xml/stream/XmlParserNextXxxTest.java | 23 +++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 42f78d211..15f059280 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -287,3 +287,6 @@ Sahana (@Sahana2524) * Contributed #891: Enforce `StreamReadConstraints.maxNestingDepth` in `FromXmlParser` (2.18.10) +* Fixed #899: Return `null` from `nextTextValue()` at end-of-input (instead of + throwing `IllegalStateException`) + (2.23.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 00e4f6bdb..712f55038 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -6,7 +6,9 @@ Project: jackson-dataformat-xml 2.23.0 (not yet released) -No changes since 2.22 +#899: Return `null` from `nextTextValue()` at end-of-input (instead of + throwing `IllegalStateException`) + (fix by @Sahana2524) 2.22.2 (16-Aug-2026) 2.22.1 (07-Jul-2026) 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 1afd42dd5..8a9273e11 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 @@ -1052,6 +1052,7 @@ public String nextTextValue() throws IOException break; case XmlTokenStream.XML_END: _updateTokenToNull(); + break; default: return _internalErrorUnknownToken(token); } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlParserNextXxxTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlParserNextXxxTest.java index 93ea9e35e..e2d75fd09 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlParserNextXxxTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlParserNextXxxTest.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class XmlParserNextXxxTest extends XmlTestUtil { @@ -57,4 +58,26 @@ public void testXmlAttributesWithNextTextValue() throws Exception assertToken(JsonToken.END_OBJECT, xp.nextToken()); // xp.close(); } + + // [dataformat-xml#899]: nextTextValue() must honor the JsonParser contract + // at end of input: return null, same as nextToken() does, instead of leaking + // an unchecked IllegalStateException from the internal XML_END branch. + @Test + public void testNextTextValueAtEndOfInput() throws Exception + { + final String XML = ""; + + FromXmlParser xp = (FromXmlParser) _xmlFactory.createParser(new StringReader(XML)); + + assertToken(JsonToken.START_OBJECT, xp.nextToken()); // + assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // max + assertEquals("7", xp.nextTextValue()); + assertToken(JsonToken.FIELD_NAME, xp.nextToken()); // offset + assertEquals("9", xp.nextTextValue()); + assertToken(JsonToken.END_OBJECT, xp.nextToken()); // + + // One more call past the end: should quietly report end-of-input + assertNull(xp.nextTextValue()); + xp.close(); + } }