Skip to content

Commit 4ff4527

Browse files
committed
Merge branch '2.12'
2 parents a90f587 + dbd1fe5 commit 4ff4527

13 files changed

+233
-76
lines changed

release-notes/CREDITS-2.x

+4
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,7 @@ Luke Korth ([email protected])
6060

6161
* Reported #366: XML containing xsi:nil is improperly parsed
6262
(2.10.2)
63+
64+
Jochen Schalanda (joschi@github)
65+
* Repoerted #318: XMLMapper fails to deserialize null (POJO reference) from blank tag
66+
(2.12.0)

release-notes/VERSION-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-dataformat-xml
44
= Releases
55
------------------------------------------------------------------------
66

7+
2.12.0 (not yet released)
8+
9+
#318: XMLMapper fails to deserialize null (POJO reference) from blank tag
10+
(reported by Jochen S)
11+
712
2.11.1 (not yet released)
813

914
-

src/main/java/com/fasterxml/jackson/dataformat/xml/XmlModule.java

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
66

77
import com.fasterxml.jackson.dataformat.xml.deser.XmlBeanDeserializerModifier;
8+
import com.fasterxml.jackson.dataformat.xml.deser.XmlBeanInstantiator;
89
import com.fasterxml.jackson.dataformat.xml.deser.XmlStringDeserializer;
910
import com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializerModifier;
1011

@@ -35,6 +36,8 @@ public void setupModule(SetupContext context)
3536
.addDeserializer(String.class, deser)
3637
.addDeserializer(CharSequence.class, deser));
3738
context.addSerializerModifier(new XmlBeanSerializerModifier());
39+
// [dataformat-xml#318]:
40+
context.addValueInstantiators(new XmlBeanInstantiator.Provider());
3841

3942
// and then bit trickier, need to add a modifier...
4043
// Need to modify BeanDeserializer, BeanSerializer that are used

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,11 @@ public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
9696
ValueInstantiator inst = deser.getValueInstantiator();
9797
// 03-Aug-2017, tatu: [dataformat-xml#254] suggests we also should
9898
// allow passing `int`/`Integer`/`long`/`Long` cases, BUT
99-
// unfortunately we can not simple use default handling. Would need
99+
// unfortunately we can not simply use default handling. Would need
100100
// coercion.
101-
if (!inst.canCreateFromString()) {
101+
// 30-Apr-2020, tatu: Complication from [dataformat-xml#318] as we now
102+
// have a delegate too...
103+
if ((inst instanceof XmlBeanInstantiator) || !inst.canCreateFromString()) {
102104
SettableBeanProperty textProp = _findSoleTextProp(config, deser.properties());
103105
if (textProp != null) {
104106
return new XmlTextDeserializer(deser, textProp);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.fasterxml.jackson.dataformat.xml.deser;
2+
3+
import java.io.IOException;
4+
5+
import com.fasterxml.jackson.databind.*;
6+
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
7+
import com.fasterxml.jackson.databind.deser.ValueInstantiators;
8+
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
9+
10+
/**
11+
* {@link ValueInstantiator} implementation needed mostly to support
12+
* creating "empty" instance (or {@code null}) of POJOs from String
13+
* value consisting of all whitespace -- something that happens with
14+
* indentation.
15+
*
16+
* @since 2.12
17+
*/
18+
public class XmlBeanInstantiator
19+
extends ValueInstantiator.Delegating
20+
implements java.io.Serializable
21+
{
22+
private static final long serialVersionUID = 1L;
23+
24+
protected final JavaType _type;
25+
26+
protected final boolean _canCreateDefault;
27+
28+
public XmlBeanInstantiator(JavaType type, ValueInstantiator base) {
29+
super(base);
30+
_type = type;
31+
_canCreateDefault = base.canCreateUsingDefault();
32+
}
33+
34+
@Override
35+
public boolean canInstantiate() { return true; }
36+
37+
@Override
38+
public boolean canCreateFromString() {
39+
return true;
40+
}
41+
42+
@Override
43+
public Object createFromString(DeserializationContext ctxt, String value)
44+
throws IOException
45+
{
46+
if (_canCreateDefault) {
47+
if (value.isEmpty() || value.trim().isEmpty()) {
48+
return delegate().createUsingDefault(ctxt);
49+
}
50+
// Should we try to give more meaningful error otherwise?
51+
}
52+
return delegate().createFromString(ctxt, value);
53+
}
54+
55+
public static class Provider extends ValueInstantiators.Base
56+
implements java.io.Serializable
57+
{
58+
private static final long serialVersionUID = 1L;
59+
60+
@Override
61+
public ValueInstantiator findValueInstantiator(DeserializationConfig config,
62+
BeanDescription beanDesc, ValueInstantiator defaultInstantiator)
63+
{
64+
// let's avoid custom ones?
65+
if (defaultInstantiator instanceof StdValueInstantiator) {
66+
return new XmlBeanInstantiator(beanDesc.getType(), defaultInstantiator);
67+
}
68+
return defaultInstantiator;
69+
}
70+
}
71+
}

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

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
package com.fasterxml.jackson.dataformat.xml.failing;
1+
package com.fasterxml.jackson.dataformat.xml.deser;
22

3-
import com.fasterxml.jackson.databind.DeserializationFeature;
43
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
54
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
65
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
@@ -43,7 +42,8 @@ public void testEmptyString() throws Exception {
4342

4443
Wrapper value = MAPPER.readValue(s, Wrapper.class);
4544
assertEquals("id", value.id);
46-
assertNull(value.nested);
45+
assertNotNull(value.nested);
46+
assertNull(value.nested.nested2);
4747
}
4848

4949
public void testBlankString() throws Exception {
@@ -57,7 +57,8 @@ public void testBlankString() throws Exception {
5757
// Cannot construct instance of `JacksonXMLTest$Nested` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (' ')
5858
Wrapper value = MAPPER.readValue(s, Wrapper.class);
5959
assertEquals("id", value.id);
60-
assertNull(value.nested);
60+
assertNotNull(value.nested);
61+
assertNull(value.nested.nested2);
6162
}
6263

6364
public void testBlankString2() throws Exception {
@@ -66,14 +67,11 @@ public void testBlankString2() throws Exception {
6667
+ " <nested> </nested>"
6768
+ "</wrapper>";
6869

69-
// This fails with the following exception:
70-
// com.fasterxml.jackson.databind.exc.MismatchedInputException:
71-
// Cannot construct instance of `JacksonXMLTest$Nested` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (' ')
7270
Wrapper value = MAPPER.readerFor(Wrapper.class)
73-
.with(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
7471
.readValue(s);
7572
assertEquals("id", value.id);
76-
assertNull(value.nested);
73+
assertNotNull(value.nested);
74+
assertNull(value.nested.nested2);
7775
}
7876

7977
public void testMissing() throws Exception {

src/test/java/com/fasterxml/jackson/dataformat/xml/failing/TestConflictingGetters.java renamed to src/test/java/com/fasterxml/jackson/dataformat/xml/failing/ConflictingGetters27Test.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
77
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
88

9-
public class TestConflictingGetters extends XmlTestBase
9+
public class ConflictingGetters27Test extends XmlTestBase
1010
{
1111
@JacksonXmlRootElement(localName = "output")
1212
static class Bean {

src/test/java/com/fasterxml/jackson/dataformat/xml/failing/EmptyListDeser124Test.java

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import java.util.List;
55

66
import com.fasterxml.jackson.annotation.JsonProperty;
7-
import com.fasterxml.jackson.annotation.JsonSetter;
8-
import com.fasterxml.jackson.annotation.Nulls;
97
import com.fasterxml.jackson.dataformat.xml.*;
108

119
// for [dataformat-xml#124]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.fasterxml.jackson.dataformat.xml.failing;
2+
3+
import java.util.*;
4+
5+
import com.fasterxml.jackson.annotation.JsonInclude;
6+
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
9+
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
10+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
11+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
12+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
13+
14+
public class Issue393DeserTest extends XmlTestBase
15+
{
16+
@JacksonXmlRootElement(localName = "result")
17+
@JsonInclude(JsonInclude.Include.NON_NULL)
18+
static class Value393 {
19+
private Prices prices = new Prices();
20+
21+
public void setPrices(Prices prices) {
22+
this.prices = prices;
23+
}
24+
25+
@JacksonXmlProperty(localName = "prices")
26+
public Prices getPrices() {
27+
return this.prices;
28+
}
29+
}
30+
31+
// @JsonIgnoreProperties(ignoreUnknown = true)
32+
@JacksonXmlRootElement(localName = "prices")
33+
static class Prices {
34+
private List<Price> price = new ArrayList<Price>();
35+
36+
public void setPrice(List<Price> price) {
37+
this.price = price;
38+
}
39+
40+
@JacksonXmlElementWrapper(useWrapping = false)
41+
@JacksonXmlProperty(localName = "price")
42+
public List<Price> getPrice() {
43+
return this.price;
44+
}
45+
}
46+
47+
// @JacksonXmlRootElement(localName = "price")
48+
// @JsonIgnoreProperties(ignoreUnknown = true)
49+
static class Price {
50+
private String price;
51+
private String num;
52+
53+
protected Price() { }
54+
public Price(String p, String n) {
55+
price = p;
56+
num = n;
57+
}
58+
59+
public void setPrice(String price) {
60+
this.price = price;
61+
}
62+
63+
@JacksonXmlProperty(localName = "price")
64+
public String getPrice() {
65+
return this.price;
66+
}
67+
68+
public void setNum(String num) {
69+
this.num = num;
70+
}
71+
72+
@JacksonXmlProperty(localName = "num")
73+
public String getNum() {
74+
return this.num;
75+
}
76+
}
77+
78+
/*
79+
/********************************************************
80+
/* Test methods
81+
/********************************************************
82+
*/
83+
84+
private final ObjectMapper MAPPER = newMapper();
85+
86+
// [dataform#393]
87+
public void testDeser393() throws Exception
88+
{
89+
// for debugging:
90+
/*
91+
Value393 input = new Value393();
92+
Prices prices = new Prices();
93+
prices.setPrice(Arrays.asList(
94+
new Price("100", "7.0"),
95+
new Price("100", "4.0")
96+
));
97+
input.setPrices(prices);
98+
99+
String xml = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(input);
100+
System.out.println("XML:\n"+xml);
101+
*/
102+
/*
103+
String content = //"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "\n" +
104+
"<result>\n"
105+
+ " <prices>\n"
106+
+ " <price>\n"
107+
+ " <num>100</num>\n"
108+
+ " <price>7.0</price>\n"
109+
+ " </price>\n"
110+
+ " <price>\n"
111+
+ " <num>100</num>\n"
112+
+ " <price>4.0</price>\n"
113+
+ " </price>"
114+
+ " </prices>\n"
115+
+ "</result>";
116+
Value393 result = MAPPER.readValue(content, Value393.class);
117+
assertNotNull(result);
118+
*/
119+
120+
String content =
121+
"<prices>\n"
122+
+ " <price>\n"
123+
+ " <num>100</num>\n"
124+
+ " <price>7.0</price>\n"
125+
+ " </price>\n"
126+
+ " <price>\n"
127+
+ " <num>100</num>\n"
128+
+ " <price>4.0</price>\n"
129+
+ " </price>"
130+
+ "</prices>\n";
131+
Prices result = MAPPER.readValue(content, Prices.class);
132+
assertNotNull(result);
133+
}
134+
}

src/test/java/com/fasterxml/jackson/dataformat/xml/failing/TestEmptyContent.java

-23
This file was deleted.

src/test/java/com/fasterxml/jackson/dataformat/xml/lists/Issue101UnwrappedListAttributesTest.java

+2-37
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,7 @@
1010
// Failing unit test(s) wrt [Issue#64]
1111
public class Issue101UnwrappedListAttributesTest extends XmlTestBase
1212
{
13-
static class Optional {
14-
@JacksonXmlText
15-
public String number = "NOT SET";
16-
17-
@JacksonXmlProperty(isAttribute=true)
18-
public String type = "NOT SET";
19-
20-
public Optional() { }
21-
22-
// uncommenting this ALSO works:
23-
// public Optional(String n) { number = n; }
24-
}
25-
26-
static class Optionals {
27-
@JacksonXmlElementWrapper(useWrapping = false)
28-
public List<Optional> optional;
29-
}
30-
31-
// For [Issue#101]
13+
// For [dataformat-xml#101]
3214
@JacksonXmlRootElement(localName = "root")
3315
@JsonPropertyOrder({ "unwrapped", "name" })
3416
static class Root {
@@ -62,24 +44,7 @@ public UnwrappedElement (String id, String type) {
6244

6345
private final XmlMapper MAPPER = new XmlMapper();
6446

65-
// // [Issue#64]
66-
public void testOptionalsWithMissingType() throws Exception
67-
{
68-
// Optionals ob = MAPPER.readValue("<MultiOptional><optional type='work'>123-456-7890</optional></MultiOptional>",
69-
Optionals ob = MAPPER.readValue("<MultiOptional><optional>123-456-7890</optional></MultiOptional>",
70-
Optionals.class);
71-
assertNotNull(ob);
72-
assertNotNull(ob.optional);
73-
assertEquals(1, ob.optional.size());
74-
75-
// System.err.println("ob: " + ob); // works fine
76-
77-
Optional opt = ob.optional.get(0);
78-
assertEquals("123-456-7890", opt.number);
79-
assertEquals("NOT SET", opt.type);
80-
}
81-
82-
// [Issue#101]
47+
// [dataformat-xml#101]
8348
public void testWithTwoAttributes() throws Exception
8449
{
8550
final String EXP = "<root>"

0 commit comments

Comments
 (0)