Skip to content

Commit 4f5fb6b

Browse files
committed
Fix #378
1 parent 85a7f10 commit 4f5fb6b

File tree

4 files changed

+50
-51
lines changed

4 files changed

+50
-51
lines changed

release-notes/VERSION-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-dataformat-xml
44
= Releases
55
------------------------------------------------------------------------
66

7+
2.10.2 (not yet released)
8+
9+
#378: Jackson 2.10.x fails to deserialize xsi:nil with multiple child elements
10+
(reported by henrik242@github)
11+
712
2.10.1 (09-Nov-2019)
813

914
- Upgrade Woodstox dependency to 6.0.2

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

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,14 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures,
185185
_parsingContext = XmlReadContext.createRootContext(-1, -1);
186186
_xmlTokens = new XmlTokenStream(xmlReader, ctxt.getSourceReference(),
187187
_formatFeatures);
188-
switch (_xmlTokens.getCurrentToken()) {
189-
case XmlTokenStream.XML_START_ELEMENT:
190-
_nextToken = JsonToken.START_OBJECT;
191-
break;
192-
case XmlTokenStream.XML_NULL:
188+
189+
// 04-Jan-2019, tatu: Root-level nulls need slightly specific handling;
190+
// changed in 2.10.2
191+
if (_xmlTokens.hasXsiNil()) {
193192
_nextToken = JsonToken.VALUE_NULL;
194-
break;
195-
default:
193+
} else if (_xmlTokens.getCurrentToken() == XmlTokenStream.XML_START_ELEMENT) {
194+
_nextToken = JsonToken.START_OBJECT;
195+
} else {
196196
_reportError("Internal problem: invalid starting state (%d)", _xmlTokens.getCurrentToken());
197197
}
198198
}
@@ -606,29 +606,28 @@ public JsonToken nextToken() throws IOException
606606
}
607607
}
608608
return (_currToken = JsonToken.VALUE_STRING);
609-
} else {
610-
// [dataformat-xml#177]: empty text may also need to be skipped
611-
// but... [dataformat-xml#191]: looks like we can't short-cut, must
612-
// loop over again
613-
if (_parsingContext.inObject()) {
614-
if ((_currToken != JsonToken.FIELD_NAME) && _isEmpty(_currText)) {
615-
try {
616-
token = _xmlTokens.next();
617-
} catch (XMLStreamException e) {
618-
StaxUtil.throwAsParseException(e, this);
619-
}
620-
continue;
609+
}
610+
// [dataformat-xml#177]: empty text may also need to be skipped
611+
// but... [dataformat-xml#191]: looks like we can't short-cut, must
612+
// loop over again
613+
if (_parsingContext.inObject()) {
614+
if ((_currToken != JsonToken.FIELD_NAME) && _isEmpty(_currText)) {
615+
try {
616+
token = _xmlTokens.next();
617+
} catch (XMLStreamException e) {
618+
StaxUtil.throwAsParseException(e, this);
621619
}
620+
continue;
622621
}
623622
}
624623
// If not a leaf (or otherwise ignorable), need to transform into property...
625624
_parsingContext.setCurrentName(_cfgNameForTextElement);
626625
_nextToken = JsonToken.VALUE_STRING;
627626
return (_currToken = JsonToken.FIELD_NAME);
628-
case XmlTokenStream.XML_NULL:
629-
return (_currToken = JsonToken.VALUE_NULL);
630627
case XmlTokenStream.XML_END:
631628
return (_currToken = null);
629+
default:
630+
return _internalErrorUnknownToken(token);
632631
}
633632
}
634633
}
@@ -754,11 +753,10 @@ public String nextTextValue() throws IOException
754753
_nextToken = JsonToken.VALUE_STRING;
755754
_currToken = JsonToken.FIELD_NAME;
756755
break;
757-
case XmlTokenStream.XML_NULL:
758-
_currToken = JsonToken.VALUE_STRING;
759-
return (_currText = null);
760756
case XmlTokenStream.XML_END:
761757
_currToken = null;
758+
default:
759+
return _internalErrorUnknownToken(token);
762760
}
763761
return null;
764762
}
@@ -782,6 +780,7 @@ private void _updateState(JsonToken t)
782780
_parsingContext.setCurrentName(_xmlTokens.getLocalName());
783781
break;
784782
default:
783+
_internalErrorUnknownToken(t);
785784
}
786785
}
787786

@@ -1052,4 +1051,8 @@ protected boolean _isEmpty(String str)
10521051
}
10531052
return true;
10541053
}
1054+
1055+
private <T> T _internalErrorUnknownToken(Object token) {
1056+
throw new IllegalStateException("Internal error: unrecognized XmlTokenStream token: "+token);
1057+
}
10551058
}

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

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public class XmlTokenStream
3131
public final static int XML_ATTRIBUTE_NAME = 3;
3232
public final static int XML_ATTRIBUTE_VALUE = 4;
3333
public final static int XML_TEXT = 5;
34-
public final static int XML_NULL = 6; // since 2.10
35-
public final static int XML_END = 7;
34+
35+
public final static int XML_END = 6;
3636

3737
// // // token replay states
3838

@@ -143,12 +143,7 @@ public XmlTokenStream(XMLStreamReader xmlReader, Object sourceRef,
143143
_formatFeatures = formatFeatures;
144144

145145
_checkXsiAttributes(); // sets _attributeCount, _nextAttributeIndex
146-
147-
if (_xsiNilFound) {
148-
_currentState = XML_NULL;
149-
} else {
150-
_currentState = XML_START_ELEMENT;
151-
}
146+
_currentState = XML_START_ELEMENT;
152147
}
153148

154149
public XMLStreamReader2 getXmlReader() {
@@ -189,9 +184,6 @@ public int next() throws XMLStreamException
189184
case XML_TEXT:
190185
System.out.println(" XML-token: XML_TEXT '"+_textValue+"'");
191186
break;
192-
case XML_NULL:
193-
System.out.println(" XML-token: XML_NULL");
194-
break;
195187
case XML_END:
196188
System.out.println(" XML-token: XML_END");
197189
break;
@@ -224,6 +216,10 @@ public void skipEndElement() throws IOException, XMLStreamException
224216
public String getLocalName() { return _localName; }
225217
public String getNamespaceURI() { return _namespaceURI; }
226218

219+
public boolean hasXsiNil() {
220+
return _xsiNilFound;
221+
}
222+
227223
/*// not used as of 2.10
228224
public boolean hasAttributes() {
229225
return (_currentState == XML_START_ELEMENT) && (_attributeCount > 0);
@@ -349,9 +345,15 @@ private final int _next() throws XMLStreamException
349345
// 06-Sep-2019, tatu: `xsi:nil` to induce "real" null value?
350346
if (_xsiNilFound) {
351347
_xsiNilFound = false;
352-
return (_currentState = XML_NULL);
348+
switch (_skipUntilTag()) {
349+
case XMLStreamConstants.END_ELEMENT:
350+
return _handleEndElement();
351+
case XMLStreamConstants.END_DOCUMENT:
352+
throw new IllegalStateException("Unexpected end-of-input after null token");
353+
default:
354+
}
355+
throw new IllegalStateException("Unexpected START_ELEMENT after null token");
353356
}
354-
355357
if (_nextAttributeIndex < _attributeCount) {
356358
_localName = _xmlReader.getAttributeLocalName(_nextAttributeIndex);
357359
_namespaceURI = _xmlReader.getAttributeNamespace(_nextAttributeIndex);
@@ -385,24 +387,12 @@ private final int _next() throws XMLStreamException
385387
return (_currentState = XML_ATTRIBUTE_VALUE);
386388
case XML_TEXT:
387389
// mixed text with other elements
388-
if (_mixedText){
390+
if (_mixedText) {
389391
_mixedText = false;
390392
return _initStartElement();
391393
}
392394
// text followed by END_ELEMENT
393395
return _handleEndElement();
394-
case XML_NULL:
395-
// at this point we are pointing to START_ELEMENT, need to find
396-
// matching END_ELEMENT, handle it
397-
// 06-Sep-2019, tatu: Should handle error cases better but for now this'll do
398-
switch (_skipUntilTag()) {
399-
case XMLStreamConstants.END_ELEMENT:
400-
return _handleEndElement();
401-
case XMLStreamConstants.END_DOCUMENT:
402-
throw new IllegalStateException("Unexpected end-of-input after null token");
403-
default:
404-
throw new IllegalStateException("Unexpected START_ELEMENT after null token");
405-
}
406396

407397
case XML_END:
408398
return XML_END;

src/test/java/com/fasterxml/jackson/dataformat/xml/failing/XsiNil378Test.java renamed to src/test/java/com/fasterxml/jackson/dataformat/xml/deser/XsiNil378Test.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.dataformat.xml.failing;
1+
package com.fasterxml.jackson.dataformat.xml.deser;
22

33
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
44
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
@@ -33,6 +33,7 @@ public void testWithStringAsNull2() throws Exception
3333

3434
bean = MAPPER.readValue(
3535
"<StringPair "+XSI_NS_DECL+"><first xsi:nil='true' /><second>not null</second></StringPair>",
36+
//"<StringPair "+XSI_NS_DECL+"><first xsi:nil='true'></first><second>not null</second></StringPair>",
3637
StringPair.class);
3738
assertNotNull(bean);
3839
assertNull(bean.first);

0 commit comments

Comments
 (0)