Skip to content

Commit

Permalink
Fix #378
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jan 5, 2020
1 parent 85a7f10 commit 4f5fb6b
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 51 deletions.
5 changes: 5 additions & 0 deletions release-notes/VERSION-2.x
Expand Up @@ -4,6 +4,11 @@ Project: jackson-dataformat-xml
= Releases
------------------------------------------------------------------------

2.10.2 (not yet released)

#378: Jackson 2.10.x fails to deserialize xsi:nil with multiple child elements
(reported by henrik242@github)

2.10.1 (09-Nov-2019)

- Upgrade Woodstox dependency to 6.0.2
Expand Down
Expand Up @@ -185,14 +185,14 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures,
_parsingContext = XmlReadContext.createRootContext(-1, -1);
_xmlTokens = new XmlTokenStream(xmlReader, ctxt.getSourceReference(),
_formatFeatures);
switch (_xmlTokens.getCurrentToken()) {
case XmlTokenStream.XML_START_ELEMENT:
_nextToken = JsonToken.START_OBJECT;
break;
case XmlTokenStream.XML_NULL:

// 04-Jan-2019, tatu: Root-level nulls need slightly specific handling;
// changed in 2.10.2
if (_xmlTokens.hasXsiNil()) {
_nextToken = JsonToken.VALUE_NULL;
break;
default:
} else if (_xmlTokens.getCurrentToken() == XmlTokenStream.XML_START_ELEMENT) {
_nextToken = JsonToken.START_OBJECT;
} else {
_reportError("Internal problem: invalid starting state (%d)", _xmlTokens.getCurrentToken());
}
}
Expand Down Expand Up @@ -606,29 +606,28 @@ public JsonToken nextToken() throws IOException
}
}
return (_currToken = JsonToken.VALUE_STRING);
} else {
// [dataformat-xml#177]: empty text may also need to be skipped
// but... [dataformat-xml#191]: looks like we can't short-cut, must
// loop over again
if (_parsingContext.inObject()) {
if ((_currToken != JsonToken.FIELD_NAME) && _isEmpty(_currText)) {
try {
token = _xmlTokens.next();
} catch (XMLStreamException e) {
StaxUtil.throwAsParseException(e, this);
}
continue;
}
// [dataformat-xml#177]: empty text may also need to be skipped
// but... [dataformat-xml#191]: looks like we can't short-cut, must
// loop over again
if (_parsingContext.inObject()) {
if ((_currToken != JsonToken.FIELD_NAME) && _isEmpty(_currText)) {
try {
token = _xmlTokens.next();
} catch (XMLStreamException e) {
StaxUtil.throwAsParseException(e, this);
}
continue;
}
}
// If not a leaf (or otherwise ignorable), need to transform into property...
_parsingContext.setCurrentName(_cfgNameForTextElement);
_nextToken = JsonToken.VALUE_STRING;
return (_currToken = JsonToken.FIELD_NAME);
case XmlTokenStream.XML_NULL:
return (_currToken = JsonToken.VALUE_NULL);
case XmlTokenStream.XML_END:
return (_currToken = null);
default:
return _internalErrorUnknownToken(token);
}
}
}
Expand Down Expand Up @@ -754,11 +753,10 @@ public String nextTextValue() throws IOException
_nextToken = JsonToken.VALUE_STRING;
_currToken = JsonToken.FIELD_NAME;
break;
case XmlTokenStream.XML_NULL:
_currToken = JsonToken.VALUE_STRING;
return (_currText = null);
case XmlTokenStream.XML_END:
_currToken = null;
default:
return _internalErrorUnknownToken(token);
}
return null;
}
Expand All @@ -782,6 +780,7 @@ private void _updateState(JsonToken t)
_parsingContext.setCurrentName(_xmlTokens.getLocalName());
break;
default:
_internalErrorUnknownToken(t);
}
}

Expand Down Expand Up @@ -1052,4 +1051,8 @@ protected boolean _isEmpty(String str)
}
return true;
}

private <T> T _internalErrorUnknownToken(Object token) {
throw new IllegalStateException("Internal error: unrecognized XmlTokenStream token: "+token);
}
}
Expand Up @@ -31,8 +31,8 @@ public class XmlTokenStream
public final static int XML_ATTRIBUTE_NAME = 3;
public final static int XML_ATTRIBUTE_VALUE = 4;
public final static int XML_TEXT = 5;
public final static int XML_NULL = 6; // since 2.10
public final static int XML_END = 7;

public final static int XML_END = 6;

// // // token replay states

Expand Down Expand Up @@ -143,12 +143,7 @@ public XmlTokenStream(XMLStreamReader xmlReader, Object sourceRef,
_formatFeatures = formatFeatures;

_checkXsiAttributes(); // sets _attributeCount, _nextAttributeIndex

if (_xsiNilFound) {
_currentState = XML_NULL;
} else {
_currentState = XML_START_ELEMENT;
}
_currentState = XML_START_ELEMENT;
}

public XMLStreamReader2 getXmlReader() {
Expand Down Expand Up @@ -189,9 +184,6 @@ public int next() throws XMLStreamException
case XML_TEXT:
System.out.println(" XML-token: XML_TEXT '"+_textValue+"'");
break;
case XML_NULL:
System.out.println(" XML-token: XML_NULL");
break;
case XML_END:
System.out.println(" XML-token: XML_END");
break;
Expand Down Expand Up @@ -224,6 +216,10 @@ public void skipEndElement() throws IOException, XMLStreamException
public String getLocalName() { return _localName; }
public String getNamespaceURI() { return _namespaceURI; }

public boolean hasXsiNil() {
return _xsiNilFound;
}

/*// not used as of 2.10
public boolean hasAttributes() {
return (_currentState == XML_START_ELEMENT) && (_attributeCount > 0);
Expand Down Expand Up @@ -349,9 +345,15 @@ private final int _next() throws XMLStreamException
// 06-Sep-2019, tatu: `xsi:nil` to induce "real" null value?
if (_xsiNilFound) {
_xsiNilFound = false;
return (_currentState = XML_NULL);
switch (_skipUntilTag()) {
case XMLStreamConstants.END_ELEMENT:
return _handleEndElement();
case XMLStreamConstants.END_DOCUMENT:
throw new IllegalStateException("Unexpected end-of-input after null token");
default:
}
throw new IllegalStateException("Unexpected START_ELEMENT after null token");
}

if (_nextAttributeIndex < _attributeCount) {
_localName = _xmlReader.getAttributeLocalName(_nextAttributeIndex);
_namespaceURI = _xmlReader.getAttributeNamespace(_nextAttributeIndex);
Expand Down Expand Up @@ -385,24 +387,12 @@ private final int _next() throws XMLStreamException
return (_currentState = XML_ATTRIBUTE_VALUE);
case XML_TEXT:
// mixed text with other elements
if (_mixedText){
if (_mixedText) {
_mixedText = false;
return _initStartElement();
}
// text followed by END_ELEMENT
return _handleEndElement();
case XML_NULL:
// at this point we are pointing to START_ELEMENT, need to find
// matching END_ELEMENT, handle it
// 06-Sep-2019, tatu: Should handle error cases better but for now this'll do
switch (_skipUntilTag()) {
case XMLStreamConstants.END_ELEMENT:
return _handleEndElement();
case XMLStreamConstants.END_DOCUMENT:
throw new IllegalStateException("Unexpected end-of-input after null token");
default:
throw new IllegalStateException("Unexpected START_ELEMENT after null token");
}

case XML_END:
return XML_END;
Expand Down
@@ -1,4 +1,4 @@
package com.fasterxml.jackson.dataformat.xml.failing;
package com.fasterxml.jackson.dataformat.xml.deser;

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

bean = MAPPER.readValue(
"<StringPair "+XSI_NS_DECL+"><first xsi:nil='true' /><second>not null</second></StringPair>",
//"<StringPair "+XSI_NS_DECL+"><first xsi:nil='true'></first><second>not null</second></StringPair>",
StringPair.class);
assertNotNull(bean);
assertNull(bean.first);
Expand Down

0 comments on commit 4f5fb6b

Please sign in to comment.