diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x
index 9a72ddfda..f1bb218f7 100644
--- a/release-notes/VERSION-2.x
+++ b/release-notes/VERSION-2.x
@@ -6,7 +6,8 @@ Project: jackson-dataformat-xml
2.11.1 (not yet released)
--
+#393: `MismatchedInputException` for nested repeating element name in `List`
+ (reported by kaizenHorse@github)
2.11.0 (26-Apr-2020)
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 fecfe36ef..10846e771 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
@@ -85,7 +85,7 @@ private Feature(boolean defaultState) {
*
* Name used for pseudo-property used for returning XML Text value (which does
* not have actual element name to use). Defaults to empty String, but
- * may be changed for interoperability reasons: JAXB, for example, uses
+ * may be changed for inter-operability reasons: JAXB, for example, uses
* "value" as name.
*
* @since 2.1
@@ -146,8 +146,6 @@ private Feature(boolean defaultState) {
protected String _currText;
- protected Set _namesToWrap;
-
/*
/**********************************************************
/* Parsing state, parsed values
@@ -323,14 +321,14 @@ public XMLStreamReader getStaxReader() {
*/
public void addVirtualWrapping(Set namesToWrap)
{
- /* 17-Sep-2012, tatu: Not 100% sure why, but this is necessary to avoid
- * problems with Lists-in-Lists properties
- */
+//System.out.println("addVirtualWrapping("+namesToWrap+")");
+ // 17-Sep-2012, tatu: Not 100% sure why, but this is necessary to avoid
+ // problems with Lists-in-Lists properties
String name = _xmlTokens.getLocalName();
if (name != null && namesToWrap.contains(name)) {
+//System.out.println("REPEAT from addVirtualWrapping()");
_xmlTokens.repeatStartElement();
}
- _namesToWrap = namesToWrap;
_parsingContext.setNamesToWrap(namesToWrap);
}
@@ -435,7 +433,7 @@ public boolean isExpectedStartArrayToken()
_currToken = JsonToken.START_ARRAY;
// Ok: must replace current context with array as well
_parsingContext.convertToArray();
-//System.out.println(" isExpectedArrayStart: OBJ->Array, wraps now: "+_parsingContext.getNamesToWrap());
+//System.out.println(" FromXmlParser.isExpectedArrayStart(): OBJ->Array");
// And just in case a field name was to be returned, wipe it
// 06-Jan-2015, tatu: Actually, could also be empty Object buffered; if so, convert...
if (_nextToken == JsonToken.END_OBJECT) {
@@ -460,13 +458,13 @@ public JsonToken nextToken() throws IOException
if (t != null) {
switch (t) {
case FIELD_NAME:
- System.out.println("JsonToken: FIELD_NAME '"+_parsingContext.getCurrentName()+"'");
+ System.out.println("FromXmlParser.nextToken(): JsonToken.FIELD_NAME '"+_parsingContext.getCurrentName()+"'");
break;
case VALUE_STRING:
- System.out.println("JsonToken: VALUE_STRING '"+getText()+"'");
+ System.out.println("FromXmlParser.nextToken(): JsonToken.VALUE_STRING '"+getText()+"'");
break;
default:
- System.out.println("JsonToken: "+t);
+ System.out.println("FromXmlParser.nextToken(): "+t);
}
}
return t;
@@ -492,7 +490,6 @@ public JsonToken nextToken() throws IOException
case END_OBJECT:
case END_ARRAY:
_parsingContext = _parsingContext.getParent();
- _namesToWrap = _parsingContext.getNamesToWrap();
break;
case FIELD_NAME:
_parsingContext.setCurrentName(_xmlTokens.getLocalName());
@@ -534,7 +531,8 @@ public JsonToken nextToken() throws IOException
// Ok: virtual wrapping can be done by simply repeating current START_ELEMENT.
// Couple of ways to do it; but start by making _xmlTokens replay the thing...
- if (_namesToWrap != null && _namesToWrap.contains(name)) {
+ if (_parsingContext.shouldWrap(name)) {
+//System.out.println("REPEAT from nextToken()");
_xmlTokens.repeatStartElement();
}
@@ -565,7 +563,6 @@ public JsonToken nextToken() throws IOException
}
_currToken = _parsingContext.inArray() ? JsonToken.END_ARRAY : JsonToken.END_OBJECT;
_parsingContext = _parsingContext.getParent();
- _namesToWrap = _parsingContext.getNamesToWrap();
return _currToken;
case XmlTokenStream.XML_ATTRIBUTE_NAME:
@@ -696,7 +693,8 @@ public String nextTextValue() throws IOException
}
String name = _xmlTokens.getLocalName();
_parsingContext.setCurrentName(name);
- if (_namesToWrap != null && _namesToWrap.contains(name)) {
+ if (_parsingContext.shouldWrap(name)) {
+//System.out.println("REPEAT from nextTextValue()");
_xmlTokens.repeatStartElement();
}
_mayBeLeaf = true;
@@ -715,7 +713,6 @@ public String nextTextValue() throws IOException
}
_currToken = _parsingContext.inArray() ? JsonToken.END_ARRAY : JsonToken.END_OBJECT;
_parsingContext = _parsingContext.getParent();
- _namesToWrap = _parsingContext.getNamesToWrap();
break;
case XmlTokenStream.XML_ATTRIBUTE_NAME:
// If there was a chance of leaf node, no more...
@@ -774,7 +771,6 @@ private void _updateState(JsonToken t)
case END_OBJECT:
case END_ARRAY:
_parsingContext = _parsingContext.getParent();
- _namesToWrap = _parsingContext.getNamesToWrap();
break;
case FIELD_NAME:
_parsingContext.setCurrentName(_xmlTokens.getLocalName());
@@ -835,7 +831,6 @@ public String getValueAsString(String defValue) throws IOException
// note: Should NOT update context, because we will still be getting
// matching END_OBJECT, which will undo contexts properly
_parsingContext = _parsingContext.getParent();
- _namesToWrap = _parsingContext.getNamesToWrap();
_currToken = JsonToken.VALUE_STRING;
_nextToken = null;
// One more thing: must explicitly skip the END_OBJECT that would follow
diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlReadContext.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlReadContext.java
index 5a341e4ae..5562a59f5 100644
--- a/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlReadContext.java
+++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlReadContext.java
@@ -176,10 +176,16 @@ public void setNamesToWrap(Set namesToWrap) {
_namesToWrap = namesToWrap;
}
- public Set getNamesToWrap() {
+ @Deprecated // since 2.11.1
+ public Set getNamesToWrap() {
return _namesToWrap;
}
+ // @since 2.11.1
+ public boolean shouldWrap(String localName) {
+ return (_namesToWrap != null) && _namesToWrap.contains(localName);
+ }
+
protected void convertToArray() {
_type = TYPE_ARRAY;
}
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 2bf3b5374..7624975fe 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
@@ -170,22 +170,22 @@ public int next() throws XMLStreamException
int n = next0();
switch (n) {
case XML_START_ELEMENT:
- System.out.println(" XML-token: XML_START_ELEMENT '"+_localName+"'");
+ System.out.println(" XmlTolenStream.next(): XML_START_ELEMENT '"+_localName+"'");
break;
case XML_END_ELEMENT:
- System.out.println(" XML-token: XML_END_ELEMENT '"+_localName+"'");
+ System.out.println(" XmlTolenStream.next(): XML_END_ELEMENT '"+_localName+"'");
break;
case XML_ATTRIBUTE_NAME:
- System.out.println(" XML-token: XML_ATTRIBUTE_NAME '"+_localName+"'");
+ System.out.println(" XmlTolenStream.next(): XML_ATTRIBUTE_NAME '"+_localName+"'");
break;
case XML_ATTRIBUTE_VALUE:
- System.out.println(" XML-token: XML_ATTRIBUTE_VALUE '"+_textValue+"'");
+ System.out.println(" XmlTolenStream.next(): XML_ATTRIBUTE_VALUE '"+_textValue+"'");
break;
case XML_TEXT:
- System.out.println(" XML-token: XML_TEXT '"+_textValue+"'");
+ System.out.println(" XmlTolenStream.next(): XML_TEXT '"+_textValue+"'");
break;
case XML_END:
- System.out.println(" XML-token: XML_END");
+ System.out.println(" XmlTolenStream.next(): XML_END");
break;
default:
throw new IllegalStateException();
@@ -194,7 +194,8 @@ public int next() throws XMLStreamException
}
*/
- public int next() throws XMLStreamException
+// public int next0() throws XMLStreamException
+ public int next() throws XMLStreamException
{
if (_repeatElement != 0) {
return (_currentState = _handleRepeatElement());
@@ -256,7 +257,7 @@ public JsonLocation getTokenLocation() {
*/
protected void repeatStartElement()
{
-//System.out.println(" -> repeatStartElement for "+_localName);
+//System.out.println(" -> repeatStartElement for "+_localName+", _currentWrapper was: "+_currentWrapper);
// sanity check: can only be used when just returned START_ELEMENT:
if (_currentState != XML_START_ELEMENT) {
throw new IllegalStateException("Current state not XML_START_ELEMENT ("
@@ -264,10 +265,11 @@ protected void repeatStartElement()
}
// Important: add wrapper, to keep track...
if (_currentWrapper == null) {
- _currentWrapper = ElementWrapper.matchingWrapper(_currentWrapper, _localName, _namespaceURI);
+ _currentWrapper = ElementWrapper.matchingWrapper(null, _localName, _namespaceURI);
} else {
_currentWrapper = ElementWrapper.matchingWrapper(_currentWrapper.getParent(), _localName, _namespaceURI);
}
+//System.out.println(" repeatStartElement for "+_localName+", _currentWrapper now: "+_currentWrapper);
_repeatElement = REPLAY_START_DUP;
}
@@ -523,12 +525,13 @@ private final int _initStartElement() throws XMLStreamException
if (_currentWrapper != null) {
if (_currentWrapper.matchesWrapper(localName, ns)) {
_currentWrapper = _currentWrapper.intermediateWrapper();
+//System.out.println(" _initStartElement(): START_ELEMENT ("+localName+") DOES match ["+_currentWrapper+"]: leave/add intermediate");
} else {
// implicit end is more interesting:
+//System.out.println(" _initStartElement(): START_ELEMENT ("+localName+") not matching '"+_localName+"'; add extra XML-END-ELEMENT!");
_localName = _currentWrapper.getWrapperLocalName();
_namespaceURI = _currentWrapper.getWrapperNamespace();
_currentWrapper = _currentWrapper.getParent();
-//System.out.println(" START_ELEMENT ("+localName+") not matching '"+_localName+"'; add extra XML-END-ELEMENT!");
// Important! We also need to restore the START_ELEMENT, so:
_nextLocalName = localName;
_nextNamespaceURI = ns;
@@ -571,16 +574,18 @@ private final void _checkXsiAttributes() {
*/
protected int _handleRepeatElement() throws XMLStreamException
{
+//System.out.println(" XMLTokenStream._handleRepeatElement()");
+
int type = _repeatElement;
_repeatElement = 0;
if (type == REPLAY_START_DUP) {
-//System.out.println("handleRepeat for START_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
+//System.out.println(" XMLTokenStream._handleRepeatElement() for START_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
// important: add the virtual element second time, but not with name to match
_currentWrapper = _currentWrapper.intermediateWrapper();
return XML_START_ELEMENT;
}
if (type == REPLAY_END) {
-//System.out.println("handleRepeat for END_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
+//System.out.println(" XMLTokenStream._handleRepeatElement() for END_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
_localName = _xmlReader.getLocalName();
_namespaceURI = _xmlReader.getNamespaceURI();
if (_currentWrapper != null) {
@@ -596,8 +601,8 @@ protected int _handleRepeatElement() throws XMLStreamException
_namespaceURI = _nextNamespaceURI;
_nextLocalName = null;
_nextNamespaceURI = null;
-
-//System.out.println("handleRepeat for START_DELAYED: "+_localName+" ("+_xmlReader.getLocalName()+")");
+
+//System.out.println(" XMLTokenStream._handleRepeatElement() for START_DELAYED: "+_localName+" ("+_xmlReader.getLocalName()+")");
return XML_START_ELEMENT;
}
@@ -606,6 +611,7 @@ protected int _handleRepeatElement() throws XMLStreamException
private final int _handleEndElement()
{
+//System.out.println(" XMLTokenStream._handleEndElement()");
if (_currentWrapper != null) {
ElementWrapper w = _currentWrapper;
// important: if we close the scope, must duplicate END_ELEMENT as well
@@ -614,7 +620,7 @@ private final int _handleEndElement()
_localName = w.getWrapperLocalName();
_namespaceURI = w.getWrapperNamespace();
_currentWrapper = _currentWrapper.getParent();
-//System.out.println(" IMPLICIT requestRepeat of END_ELEMENT '"+_localName);
+//System.out.println(" XMLTokenStream._handleEndElement(): IMPLICIT requestRepeat of END_ELEMENT '"+_localName);
} else {
_currentWrapper = _currentWrapper.getParent();
}
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/Issue393DeserTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/Issue393DeserTest.java
index c179737e1..d4b7210ce 100644
--- a/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/Issue393DeserTest.java
+++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/Issue393DeserTest.java
@@ -28,7 +28,6 @@ public Prices getPrices() {
}
}
-// @JsonIgnoreProperties(ignoreUnknown = true)
@JacksonXmlRootElement(localName = "prices")
static class Prices {
private List price = new ArrayList();
@@ -38,14 +37,11 @@ public void setPrice(List price) {
}
@JacksonXmlElementWrapper(useWrapping = false)
- @JacksonXmlProperty(localName = "price")
public List getPrice() {
return this.price;
}
}
-// @JacksonXmlRootElement(localName = "price")
-// @JsonIgnoreProperties(ignoreUnknown = true)
static class Price {
private String price;
private String num;
@@ -60,7 +56,6 @@ public void setPrice(String price) {
this.price = price;
}
- @JacksonXmlProperty(localName = "price")
public String getPrice() {
return this.price;
}
@@ -69,7 +64,6 @@ public void setNum(String num) {
this.num = num;
}
- @JacksonXmlProperty(localName = "num")
public String getNum() {
return this.num;
}