Skip to content

Commit c1db62b

Browse files
committed
Merge branch '2.11' into 2.12
2 parents 09e141d + 242c869 commit c1db62b

File tree

7 files changed

+55
-45
lines changed

7 files changed

+55
-45
lines changed

release-notes/CREDITS-2.x

+7-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ Luke Korth ([email protected])
6161
* Reported #366: XML containing xsi:nil is improperly parsed
6262
(2.10.2)
6363

64+
Alexei Volkov (softkot@github)
65+
66+
* Reported #294: XML parser error with nested same element names
67+
(2.11.1)
68+
6469
Jochen Schalanda (joschi@github)
65-
* Repoerted #318: XMLMapper fails to deserialize null (POJO reference) from blank tag
70+
71+
* Reported #318: XMLMapper fails to deserialize null (POJO reference) from blank tag
6672
(2.12.0)

release-notes/VERSION-2.x

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ Project: jackson-dataformat-xml
1111

1212
2.11.1 (not yet released)
1313

14-
-
14+
#294: XML parser error with nested same element names
15+
(reported by Alexei V)
16+
#393: `MismatchedInputException` for nested repeating element name in `List`
17+
(reported by kaizenHorse@github)
1518

1619
2.11.0 (26-Apr-2020)
1720

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

+13-18
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private Feature(boolean defaultState) {
8585
*<p>
8686
* Name used for pseudo-property used for returning XML Text value (which does
8787
* not have actual element name to use). Defaults to empty String, but
88-
* may be changed for interoperability reasons: JAXB, for example, uses
88+
* may be changed for inter-operability reasons: JAXB, for example, uses
8989
* "value" as name.
9090
*
9191
* @since 2.1
@@ -146,8 +146,6 @@ private Feature(boolean defaultState) {
146146

147147
protected String _currText;
148148

149-
protected Set<String> _namesToWrap;
150-
151149
/*
152150
/**********************************************************
153151
/* Parsing state, parsed values
@@ -323,14 +321,14 @@ public XMLStreamReader getStaxReader() {
323321
*/
324322
public void addVirtualWrapping(Set<String> namesToWrap)
325323
{
326-
/* 17-Sep-2012, tatu: Not 100% sure why, but this is necessary to avoid
327-
* problems with Lists-in-Lists properties
328-
*/
324+
//System.out.println("addVirtualWrapping("+namesToWrap+")");
325+
// 17-Sep-2012, tatu: Not 100% sure why, but this is necessary to avoid
326+
// problems with Lists-in-Lists properties
329327
String name = _xmlTokens.getLocalName();
330328
if (name != null && namesToWrap.contains(name)) {
329+
//System.out.println("REPEAT from addVirtualWrapping()");
331330
_xmlTokens.repeatStartElement();
332331
}
333-
_namesToWrap = namesToWrap;
334332
_parsingContext.setNamesToWrap(namesToWrap);
335333
}
336334

@@ -435,7 +433,7 @@ public boolean isExpectedStartArrayToken()
435433
_currToken = JsonToken.START_ARRAY;
436434
// Ok: must replace current context with array as well
437435
_parsingContext.convertToArray();
438-
//System.out.println(" isExpectedArrayStart: OBJ->Array, wraps now: "+_parsingContext.getNamesToWrap());
436+
//System.out.println(" FromXmlParser.isExpectedArrayStart(): OBJ->Array");
439437
// And just in case a field name was to be returned, wipe it
440438
// 06-Jan-2015, tatu: Actually, could also be empty Object buffered; if so, convert...
441439
if (_nextToken == JsonToken.END_OBJECT) {
@@ -460,13 +458,13 @@ public JsonToken nextToken() throws IOException
460458
if (t != null) {
461459
switch (t) {
462460
case FIELD_NAME:
463-
System.out.println("JsonToken: FIELD_NAME '"+_parsingContext.getCurrentName()+"'");
461+
System.out.println("FromXmlParser.nextToken(): JsonToken.FIELD_NAME '"+_parsingContext.getCurrentName()+"'");
464462
break;
465463
case VALUE_STRING:
466-
System.out.println("JsonToken: VALUE_STRING '"+getText()+"'");
464+
System.out.println("FromXmlParser.nextToken(): JsonToken.VALUE_STRING '"+getText()+"'");
467465
break;
468466
default:
469-
System.out.println("JsonToken: "+t);
467+
System.out.println("FromXmlParser.nextToken(): "+t);
470468
}
471469
}
472470
return t;
@@ -492,7 +490,6 @@ public JsonToken nextToken() throws IOException
492490
case END_OBJECT:
493491
case END_ARRAY:
494492
_parsingContext = _parsingContext.getParent();
495-
_namesToWrap = _parsingContext.getNamesToWrap();
496493
break;
497494
case FIELD_NAME:
498495
_parsingContext.setCurrentName(_xmlTokens.getLocalName());
@@ -534,7 +531,8 @@ public JsonToken nextToken() throws IOException
534531

535532
// Ok: virtual wrapping can be done by simply repeating current START_ELEMENT.
536533
// Couple of ways to do it; but start by making _xmlTokens replay the thing...
537-
if (_namesToWrap != null && _namesToWrap.contains(name)) {
534+
if (_parsingContext.shouldWrap(name)) {
535+
//System.out.println("REPEAT from nextToken()");
538536
_xmlTokens.repeatStartElement();
539537
}
540538

@@ -565,7 +563,6 @@ public JsonToken nextToken() throws IOException
565563
}
566564
_currToken = _parsingContext.inArray() ? JsonToken.END_ARRAY : JsonToken.END_OBJECT;
567565
_parsingContext = _parsingContext.getParent();
568-
_namesToWrap = _parsingContext.getNamesToWrap();
569566
return _currToken;
570567

571568
case XmlTokenStream.XML_ATTRIBUTE_NAME:
@@ -696,7 +693,8 @@ public String nextTextValue() throws IOException
696693
}
697694
String name = _xmlTokens.getLocalName();
698695
_parsingContext.setCurrentName(name);
699-
if (_namesToWrap != null && _namesToWrap.contains(name)) {
696+
if (_parsingContext.shouldWrap(name)) {
697+
//System.out.println("REPEAT from nextTextValue()");
700698
_xmlTokens.repeatStartElement();
701699
}
702700
_mayBeLeaf = true;
@@ -715,7 +713,6 @@ public String nextTextValue() throws IOException
715713
}
716714
_currToken = _parsingContext.inArray() ? JsonToken.END_ARRAY : JsonToken.END_OBJECT;
717715
_parsingContext = _parsingContext.getParent();
718-
_namesToWrap = _parsingContext.getNamesToWrap();
719716
break;
720717
case XmlTokenStream.XML_ATTRIBUTE_NAME:
721718
// If there was a chance of leaf node, no more...
@@ -774,7 +771,6 @@ private void _updateState(JsonToken t)
774771
case END_OBJECT:
775772
case END_ARRAY:
776773
_parsingContext = _parsingContext.getParent();
777-
_namesToWrap = _parsingContext.getNamesToWrap();
778774
break;
779775
case FIELD_NAME:
780776
_parsingContext.setCurrentName(_xmlTokens.getLocalName());
@@ -835,7 +831,6 @@ public String getValueAsString(String defValue) throws IOException
835831
// note: Should NOT update context, because we will still be getting
836832
// matching END_OBJECT, which will undo contexts properly
837833
_parsingContext = _parsingContext.getParent();
838-
_namesToWrap = _parsingContext.getNamesToWrap();
839834
_currToken = JsonToken.VALUE_STRING;
840835
_nextToken = null;
841836
// One more thing: must explicitly skip the END_OBJECT that would follow

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,16 @@ public void setNamesToWrap(Set<String> namesToWrap) {
176176
_namesToWrap = namesToWrap;
177177
}
178178

179-
public Set<String> getNamesToWrap() {
179+
@Deprecated // since 2.11.1
180+
public Set<String> getNamesToWrap() {
180181
return _namesToWrap;
181182
}
182183

184+
// @since 2.11.1
185+
public boolean shouldWrap(String localName) {
186+
return (_namesToWrap != null) && _namesToWrap.contains(localName);
187+
}
188+
183189
protected void convertToArray() {
184190
_type = TYPE_ARRAY;
185191
}

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

+21-15
Original file line numberDiff line numberDiff line change
@@ -170,22 +170,22 @@ public int next() throws XMLStreamException
170170
int n = next0();
171171
switch (n) {
172172
case XML_START_ELEMENT:
173-
System.out.println(" XML-token: XML_START_ELEMENT '"+_localName+"'");
173+
System.out.println(" XmlTolenStream.next(): XML_START_ELEMENT '"+_localName+"'");
174174
break;
175175
case XML_END_ELEMENT:
176-
System.out.println(" XML-token: XML_END_ELEMENT '"+_localName+"'");
176+
System.out.println(" XmlTolenStream.next(): XML_END_ELEMENT '"+_localName+"'");
177177
break;
178178
case XML_ATTRIBUTE_NAME:
179-
System.out.println(" XML-token: XML_ATTRIBUTE_NAME '"+_localName+"'");
179+
System.out.println(" XmlTolenStream.next(): XML_ATTRIBUTE_NAME '"+_localName+"'");
180180
break;
181181
case XML_ATTRIBUTE_VALUE:
182-
System.out.println(" XML-token: XML_ATTRIBUTE_VALUE '"+_textValue+"'");
182+
System.out.println(" XmlTolenStream.next(): XML_ATTRIBUTE_VALUE '"+_textValue+"'");
183183
break;
184184
case XML_TEXT:
185-
System.out.println(" XML-token: XML_TEXT '"+_textValue+"'");
185+
System.out.println(" XmlTolenStream.next(): XML_TEXT '"+_textValue+"'");
186186
break;
187187
case XML_END:
188-
System.out.println(" XML-token: XML_END");
188+
System.out.println(" XmlTolenStream.next(): XML_END");
189189
break;
190190
default:
191191
throw new IllegalStateException();
@@ -194,7 +194,8 @@ public int next() throws XMLStreamException
194194
}
195195
*/
196196

197-
public int next() throws XMLStreamException
197+
// public int next0() throws XMLStreamException
198+
public int next() throws XMLStreamException
198199
{
199200
if (_repeatElement != 0) {
200201
return (_currentState = _handleRepeatElement());
@@ -256,18 +257,19 @@ public JsonLocation getTokenLocation() {
256257
*/
257258
protected void repeatStartElement()
258259
{
259-
//System.out.println(" -> repeatStartElement for "+_localName);
260+
//System.out.println(" -> repeatStartElement for "+_localName+", _currentWrapper was: "+_currentWrapper);
260261
// sanity check: can only be used when just returned START_ELEMENT:
261262
if (_currentState != XML_START_ELEMENT) {
262263
throw new IllegalStateException("Current state not XML_START_ELEMENT ("
263264
+XML_START_ELEMENT+") but "+_currentState);
264265
}
265266
// Important: add wrapper, to keep track...
266267
if (_currentWrapper == null) {
267-
_currentWrapper = ElementWrapper.matchingWrapper(_currentWrapper, _localName, _namespaceURI);
268+
_currentWrapper = ElementWrapper.matchingWrapper(null, _localName, _namespaceURI);
268269
} else {
269270
_currentWrapper = ElementWrapper.matchingWrapper(_currentWrapper.getParent(), _localName, _namespaceURI);
270271
}
272+
//System.out.println(" repeatStartElement for "+_localName+", _currentWrapper now: "+_currentWrapper);
271273
_repeatElement = REPLAY_START_DUP;
272274
}
273275

@@ -523,12 +525,13 @@ private final int _initStartElement() throws XMLStreamException
523525
if (_currentWrapper != null) {
524526
if (_currentWrapper.matchesWrapper(localName, ns)) {
525527
_currentWrapper = _currentWrapper.intermediateWrapper();
528+
//System.out.println(" _initStartElement(): START_ELEMENT ("+localName+") DOES match ["+_currentWrapper+"]: leave/add intermediate");
526529
} else {
527530
// implicit end is more interesting:
531+
//System.out.println(" _initStartElement(): START_ELEMENT ("+localName+") not matching '"+_localName+"'; add extra XML-END-ELEMENT!");
528532
_localName = _currentWrapper.getWrapperLocalName();
529533
_namespaceURI = _currentWrapper.getWrapperNamespace();
530534
_currentWrapper = _currentWrapper.getParent();
531-
//System.out.println(" START_ELEMENT ("+localName+") not matching '"+_localName+"'; add extra XML-END-ELEMENT!");
532535
// Important! We also need to restore the START_ELEMENT, so:
533536
_nextLocalName = localName;
534537
_nextNamespaceURI = ns;
@@ -571,16 +574,18 @@ private final void _checkXsiAttributes() {
571574
*/
572575
protected int _handleRepeatElement() throws XMLStreamException
573576
{
577+
//System.out.println(" XMLTokenStream._handleRepeatElement()");
578+
574579
int type = _repeatElement;
575580
_repeatElement = 0;
576581
if (type == REPLAY_START_DUP) {
577-
//System.out.println("handleRepeat for START_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
582+
//System.out.println(" XMLTokenStream._handleRepeatElement() for START_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
578583
// important: add the virtual element second time, but not with name to match
579584
_currentWrapper = _currentWrapper.intermediateWrapper();
580585
return XML_START_ELEMENT;
581586
}
582587
if (type == REPLAY_END) {
583-
//System.out.println("handleRepeat for END_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
588+
//System.out.println(" XMLTokenStream._handleRepeatElement() for END_ELEMENT: "+_localName+" ("+_xmlReader.getLocalName()+")");
584589
_localName = _xmlReader.getLocalName();
585590
_namespaceURI = _xmlReader.getNamespaceURI();
586591
if (_currentWrapper != null) {
@@ -596,8 +601,8 @@ protected int _handleRepeatElement() throws XMLStreamException
596601
_namespaceURI = _nextNamespaceURI;
597602
_nextLocalName = null;
598603
_nextNamespaceURI = null;
599-
600-
//System.out.println("handleRepeat for START_DELAYED: "+_localName+" ("+_xmlReader.getLocalName()+")");
604+
605+
//System.out.println(" XMLTokenStream._handleRepeatElement() for START_DELAYED: "+_localName+" ("+_xmlReader.getLocalName()+")");
601606

602607
return XML_START_ELEMENT;
603608
}
@@ -606,6 +611,7 @@ protected int _handleRepeatElement() throws XMLStreamException
606611

607612
private final int _handleEndElement()
608613
{
614+
//System.out.println(" XMLTokenStream._handleEndElement()");
609615
if (_currentWrapper != null) {
610616
ElementWrapper w = _currentWrapper;
611617
// important: if we close the scope, must duplicate END_ELEMENT as well
@@ -614,7 +620,7 @@ private final int _handleEndElement()
614620
_localName = w.getWrapperLocalName();
615621
_namespaceURI = w.getWrapperNamespace();
616622
_currentWrapper = _currentWrapper.getParent();
617-
//System.out.println(" IMPLICIT requestRepeat of END_ELEMENT '"+_localName);
623+
//System.out.println(" XMLTokenStream._handleEndElement(): IMPLICIT requestRepeat of END_ELEMENT '"+_localName);
618624
} else {
619625
_currentWrapper = _currentWrapper.getParent();
620626
}

src/test/java/com/fasterxml/jackson/dataformat/xml/failing/ListDeser294Test.java renamed to src/test/java/com/fasterxml/jackson/dataformat/xml/lists/ListDeser294Test.java

+1-1
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.lists;
22

33
import java.util.*;
44

src/test/java/com/fasterxml/jackson/dataformat/xml/failing/Issue393DeserTest.java renamed to src/test/java/com/fasterxml/jackson/dataformat/xml/lists/ListDeser393Test.java

+2-8
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.lists;
22

33
import java.util.*;
44

@@ -11,7 +11,7 @@
1111
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
1212
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
1313

14-
public class Issue393DeserTest extends XmlTestBase
14+
public class ListDeser393Test extends XmlTestBase
1515
{
1616
@JacksonXmlRootElement(localName = "result")
1717
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -28,7 +28,6 @@ public Prices getPrices() {
2828
}
2929
}
3030

31-
// @JsonIgnoreProperties(ignoreUnknown = true)
3231
@JacksonXmlRootElement(localName = "prices")
3332
static class Prices {
3433
private List<Price> price = new ArrayList<Price>();
@@ -38,14 +37,11 @@ public void setPrice(List<Price> price) {
3837
}
3938

4039
@JacksonXmlElementWrapper(useWrapping = false)
41-
@JacksonXmlProperty(localName = "price")
4240
public List<Price> getPrice() {
4341
return this.price;
4442
}
4543
}
4644

47-
// @JacksonXmlRootElement(localName = "price")
48-
// @JsonIgnoreProperties(ignoreUnknown = true)
4945
static class Price {
5046
private String price;
5147
private String num;
@@ -60,7 +56,6 @@ public void setPrice(String price) {
6056
this.price = price;
6157
}
6258

63-
@JacksonXmlProperty(localName = "price")
6459
public String getPrice() {
6560
return this.price;
6661
}
@@ -69,7 +64,6 @@ public void setNum(String num) {
6964
this.num = num;
7065
}
7166

72-
@JacksonXmlProperty(localName = "num")
7367
public String getNum() {
7468
return this.num;
7569
}

0 commit comments

Comments
 (0)