Skip to content

Commit 0cea5b0

Browse files
committed
Fix #491 and #538
1 parent 9fcd1af commit 0cea5b0

File tree

10 files changed

+112
-16
lines changed

10 files changed

+112
-16
lines changed

release-notes/CREDITS-2.x

+6
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,9 @@ Jonas Konrad (@yawkat)
189189

190190
* Contributed fix for #545: `@JacksonXmlText` does not work when paired with `@JsonRawValue`
191191
(2.14.0)
192+
193+
Volkan Yazıcı (vy@github)
194+
195+
* Reported #491: `XmlMapper` 2.12 regression: no default no-arg ctor found
196+
(2.14.0)
197+

release-notes/VERSION-2.x

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Project: jackson-dataformat-xml
66

77
2.14.0 (not yet released)
88

9+
#491: `XmlMapper` 2.12 regression: no default no-arg ctor found
10+
(reported by Volkan Y)
11+
#538: Required attribute of `@JsonProperty` is ignored when deserializing from XML
12+
(reported by johandeschutterGET@github)
913
#545: `@JacksonXmlText` does not work when paired with `@JsonRawValue`
1014
(reported by James D)
1115
(fix contributed by Jonas K)

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ public FromXmlParser(IOContext ctxt, int genericParserFeatures, int xmlFeatures,
278278
} else {
279279
switch (firstToken) {
280280
case XmlTokenStream.XML_START_ELEMENT:
281-
case XmlTokenStream.XML_DELAYED_START_ELEMENT:
281+
// Removed from 2.14:
282+
// case XmlTokenStream.XML_DELAYED_START_ELEMENT:
282283
_nextToken = JsonToken.START_OBJECT;
283284
break;
284285
case XmlTokenStream.XML_ROOT_TEXT:

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

+26-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class XmlTokenStream
3535

3636
// New in 2.12: needed to "re-process" previously encountered START_ELEMENT,
3737
// with possible leading text
38-
public final static int XML_DELAYED_START_ELEMENT = 6;
38+
// public final static int XML_DELAYED_START_ELEMENT = 6;
3939

4040
// 2.12 also exposes "root scalars" as-is, instead of wrapping as Objects; this
4141
// needs some more state management too
@@ -180,11 +180,26 @@ public int initialize() throws XMLStreamException
180180

181181
// 02-Jul-2020, tatu: Two choices: if child elements OR attributes, expose
182182
// as Object value; otherwise expose as Text
183-
if (_xsiNilFound || _attributeCount > 0) {
184-
return (_currentState = XML_START_ELEMENT);
183+
// 06-Sep-2022, tatu: Actually expose as Object in almost every situation
184+
// as of 2.14: otherwise we have lots of issues with empty POJOs,
185+
// Lists, Maps
186+
if (_xmlReader.isEmptyElement()
187+
&& FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL.enabledIn(_formatFeatures)
188+
&& !_xsiNilFound
189+
&& _attributeCount < 1) {
190+
// 06-Sep-2022, tatu: In fact the only special case of null conversion
191+
// of the root empty element
192+
_textValue = null;
193+
_startElementAfterText = false;
194+
return (_currentState = XML_ROOT_TEXT);
185195
}
196+
return (_currentState = XML_START_ELEMENT);
197+
198+
// 06-Sep-2022, tatu: This code was used in 2.12, 2.13, may be
199+
// removed after 2.14 if/when no longer needed
186200

187201
// copied from START_ELEMENT section of _next():
202+
/*
188203
final String text = _collectUntilTag();
189204
if (text == null) {
190205
// 30-Nov-2020, tatu: [dataformat-xml#435], this is tricky
@@ -208,6 +223,7 @@ public int initialize() throws XMLStreamException
208223
_startElementAfterText = false;
209224
_textValue = text;
210225
return (_currentState = XML_ROOT_TEXT);
226+
*/
211227
}
212228

213229
public XMLStreamReader2 getXmlReader() {
@@ -396,9 +412,12 @@ protected void skipAttributes()
396412
break;
397413
case XML_TEXT:
398414
break; // nothing to do... is it even legal?
415+
416+
/*
399417
case XML_DELAYED_START_ELEMENT:
400418
// 03-Jul-2020, tatu: and here nothing to do either... ?
401419
break;
420+
*/
402421
default:
403422
throw new IllegalStateException(
404423
"Current state not XML_START_ELEMENT or XML_ATTRIBUTE_NAME but "+_currentStateDesc());
@@ -459,6 +478,7 @@ private final int _next() throws XMLStreamException
459478
_startElementAfterText = false;
460479
return _handleEndElement();
461480

481+
/*
462482
case XML_DELAYED_START_ELEMENT: // since 2.12, to support scalar Root Value
463483
// Two cases: either "simple" with not text
464484
if (_textValue == null) {
@@ -468,6 +488,7 @@ private final int _next() throws XMLStreamException
468488
// then followed by start element
469489
_startElementAfterText = true;
470490
return (_currentState = XML_TEXT);
491+
*/
471492

472493
case XML_ATTRIBUTE_NAME:
473494
// if we just returned name, will need to just send value next
@@ -785,8 +806,8 @@ protected String _stateDesc(int state) {
785806
return "XML_ATTRIBUTE_VALUE";
786807
case XML_TEXT:
787808
return "XML_TEXT";
788-
case XML_DELAYED_START_ELEMENT:
789-
return "XML_START_ELEMENT_DELAYED";
809+
// case XML_DELAYED_START_ELEMENT:
810+
// return "XML_START_ELEMENT_DELAYED";
790811
case XML_ROOT_TEXT:
791812
return "XML_ROOT_TEXT";
792813
case XML_END:

src/test/java/com/fasterxml/jackson/dataformat/xml/deser/SimpleStringValuesTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ public void testSimpleStringElement() throws Exception
4141
// empty String can be used in two different ways... behavior change b/w 2.11
4242
// and 2.12; 2.11 would lead to coercion from empty Object into default-ctor
4343
// build Bean, but with gets empty String, passed via String-creator.
44+
// 06-Sep-2022, tatu: With 2.14 behavior should become closer to 2.11 in
45+
// this respect.
4446
public void testMissingString() throws Exception
4547
{
4648
StringBean bean = MAPPER.readValue("<StringBean />", StringBean.class);
4749
assertNotNull(bean);
48-
assertEquals("", bean.text);
50+
assertEquals(new StringBean().text, bean.text);
4951
}
5052

5153
/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.fasterxml.jackson.dataformat.xml.failing;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.fasterxml.jackson.annotation.JsonRootName;
6+
7+
import com.fasterxml.jackson.databind.*;
8+
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
9+
import com.fasterxml.jackson.dataformat.xml.*;
10+
11+
public class PojoWithRequired538Test extends XmlTestBase
12+
{
13+
@JsonRootName(value = "bar")
14+
static class Bar538
15+
{
16+
int foo;
17+
18+
public Bar538() { }
19+
20+
@JsonCreator
21+
public Bar538(@JsonProperty(value = "foo", required = true) final int foo)
22+
{
23+
this.foo = foo;
24+
}
25+
}
26+
27+
/*
28+
/**********************************************************************
29+
/* Test methods
30+
/**********************************************************************
31+
*/
32+
33+
private final XmlMapper MAPPER = mapperBuilder()
34+
.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)
35+
.build();
36+
37+
// [dataformat-xml#538]
38+
public void testPojoWithRequiredFromEmpty() throws Exception
39+
{
40+
// Should fail
41+
try {
42+
MAPPER.readValue("<bar></bar>", Bar538.class);
43+
fail("Should not pass");
44+
} catch (MismatchedInputException e) {
45+
verifyException(e, "Missing required creator property 'foo'");
46+
}
47+
}
48+
}

src/test/java/com/fasterxml/jackson/dataformat/xml/misc/TextValueTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ public void testAlternateTextElementName() throws IOException
140140
MAPPER.readValue(XML, JAXBStyle.class);
141141
fail("Should have failed");
142142
} catch (MismatchedInputException e) {
143-
verifyException(e, "Cannot construct instance of");
143+
// verifyException(e, "Cannot construct instance of");
144+
verifyException(e, "Unrecognized field");
144145
}
145146
JacksonXmlModule module = new JacksonXmlModule();
146147
module.setXMLTextElementName("value");

src/test/java/com/fasterxml/jackson/dataformat/xml/stream/FormatDetectionTest.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ public void testSimpleValidXmlDecl() throws Exception
4848
assertEquals(MatchStrength.FULL_MATCH, matcher.getMatchStrength());
4949
// ensure we could build a parser...
5050
try (JsonParser p = matcher.createParserWithMatch()) {
51-
assertToken(JsonToken.VALUE_STRING, p.nextToken());
51+
assertToken(JsonToken.START_OBJECT, p.nextToken());
52+
assertToken(JsonToken.END_OBJECT, p.nextToken());
53+
assertNull(p.nextToken());
5254
}
5355
}
5456

@@ -63,7 +65,10 @@ public void testSimpleValidRoot() throws Exception
6365
assertEquals(MatchStrength.SOLID_MATCH, matcher.getMatchStrength());
6466
// ensure we could build a parser...
6567
try (JsonParser p = matcher.createParserWithMatch()) {
66-
assertToken(JsonToken.VALUE_STRING, p.nextToken());
68+
// assertToken(JsonToken.VALUE_STRING, p.nextToken());
69+
assertToken(JsonToken.START_OBJECT, p.nextToken());
70+
assertToken(JsonToken.END_OBJECT, p.nextToken());
71+
assertNull(p.nextToken());
6772
}
6873
}
6974

@@ -78,7 +83,9 @@ public void testSimpleValidDoctype() throws Exception
7883
assertEquals(MatchStrength.SOLID_MATCH, matcher.getMatchStrength());
7984
// ensure we could build a parser...
8085
try (JsonParser p = matcher.createParserWithMatch()) {
81-
assertToken(JsonToken.VALUE_STRING, p.nextToken());
86+
assertToken(JsonToken.START_OBJECT, p.nextToken());
87+
assertToken(JsonToken.END_OBJECT, p.nextToken());
88+
assertNull(p.nextToken());
8289
}
8390
}
8491

src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlParserTest.java

+4
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,12 @@ public void testRootScalar() throws Exception
8181
// 02-Jul-2020, tatu: Does not work quite yet
8282
final String XML = "<data>value</data>";
8383
try (JsonParser p = _xmlMapper.createParser(XML)) {
84+
assertToken(JsonToken.START_OBJECT, p.nextToken());
85+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
86+
assertEquals("", p.currentName());
8487
assertToken(JsonToken.VALUE_STRING, p.nextToken());
8588
assertEquals("value", p.getText());
89+
assertToken(JsonToken.END_OBJECT, p.nextToken());
8690
assertNull(p.nextToken());
8791
// should be ok to call again tho
8892
assertNull(p.nextToken());

src/test/java/com/fasterxml/jackson/dataformat/xml/stream/XmlTokenStreamTest.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
1212
import com.fasterxml.jackson.dataformat.xml.deser.XmlTokenStream;
1313

14+
15+
// NOTE: test changed a lot between 2.13 and 2.14:
1416
public class XmlTokenStreamTest extends XmlTestBase
1517
{
1618
private final XmlFactory XML_FACTORY = newMapper().getFactory();
1719

1820
public void testSimple() throws Exception
1921
{
2022
XmlTokenStream tokens = _tokensFor("<root><leaf id='123'>abc</leaf></root>");
21-
assertEquals(XmlTokenStream.XML_DELAYED_START_ELEMENT, tokens.getCurrentToken());
23+
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
2224
assertEquals("root", tokens.getLocalName());
2325
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.next());
2426
assertEquals("leaf", tokens.getLocalName());
@@ -82,7 +84,7 @@ private void _testEmptyTags(boolean emptyAsNull) throws Exception
8284
f &= ~FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL.getMask();
8385
}
8486
XmlTokenStream tokens = _tokensFor(XML, f);
85-
assertEquals(XmlTokenStream.XML_DELAYED_START_ELEMENT, tokens.getCurrentToken());
87+
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
8688
assertEquals("root", tokens.getLocalName());
8789
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.next());
8890
assertEquals("leaf", tokens.getLocalName());
@@ -98,7 +100,7 @@ private void _testEmptyTags(boolean emptyAsNull) throws Exception
98100
public void testNested() throws Exception
99101
{
100102
XmlTokenStream tokens = _tokensFor( "<root><a><b><c>abc</c></b></a></root>");
101-
assertEquals(XmlTokenStream.XML_DELAYED_START_ELEMENT, tokens.getCurrentToken());
103+
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
102104
assertEquals("root", tokens.getLocalName());
103105
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.next());
104106
assertEquals("a", tokens.getLocalName());
@@ -120,7 +122,7 @@ public void testMixedContentBetween() throws Exception
120122
{
121123
XmlTokenStream tokens = _tokensFor("<root>first<a>123</a> and second <b>abc</b>\n</root>");
122124

123-
assertEquals(XmlTokenStream.XML_DELAYED_START_ELEMENT, tokens.getCurrentToken());
125+
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
124126
assertEquals("root", tokens.getLocalName());
125127

126128
assertEquals(XmlTokenStream.XML_TEXT, tokens.next());
@@ -150,7 +152,7 @@ public void testMixedContentAfter() throws Exception
150152
{
151153
XmlTokenStream tokens = _tokensFor("<root>first<a>123</a>last &amp; final</root>");
152154

153-
assertEquals(XmlTokenStream.XML_DELAYED_START_ELEMENT, tokens.getCurrentToken());
155+
assertEquals(XmlTokenStream.XML_START_ELEMENT, tokens.getCurrentToken());
154156
assertEquals("root", tokens.getLocalName());
155157

156158
assertEquals(XmlTokenStream.XML_TEXT, tokens.next());

0 commit comments

Comments
 (0)