Skip to content

Commit b2e92b6

Browse files
committed
Fix #3005
1 parent 2c85bd6 commit b2e92b6

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

release-notes/VERSION-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Project: jackson-databind
66

77
2.12.2 (not yet released)
88

9+
#3005: String property deserializes null as "null" for
10+
`JsonTypeInfo.As.EXTERNAL_PROPERTY`
911
#3022: Property ignorals cause `BeanDeserializer `to forget how to read
1012
from arrays (not copying `_arrayDelegateDeserializer`)
1113
(reported by Gian M)

src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ public boolean handlePropertyValue(JsonParser p, DeserializationContext ctxt,
172172
ExtTypedProperty prop = _properties[index];
173173
boolean canDeserialize;
174174
if (prop.hasTypePropertyName(propName)) {
175-
_typeIds[index] = p.getText();
175+
// 19-Feb-2021, tatu: as per [databind#3005], don't use "getText()"
176+
// since that'll coerce null value into String "null"...
177+
_typeIds[index] = p.getValueAsString();
176178
p.skipChildren();
177179
canDeserialize = (bean != null) && (_tokens[index] != null);
178180
} else {
@@ -270,7 +272,12 @@ public Object complete(JsonParser p, DeserializationContext ctxt,
270272
final ExtTypedProperty extProp = _properties[i];
271273
if (typeId == null) {
272274
// let's allow missing both type and property (may already have been set, too)
273-
if (_tokens[i] == null) {
275+
TokenBuffer tb = _tokens[i];
276+
if ((tb == null)
277+
// 19-Feb-2021, tatu: Both missing value and explicit `null`
278+
// should be accepted...
279+
|| (tb.firstToken() == JsonToken.VALUE_NULL)
280+
) {
274281
continue;
275282
}
276283
// but not just one

src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java

+11
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,17 @@ public JsonToken firstToken() {
299299
return _first.type(0);
300300
}
301301

302+
/**
303+
* Accessor for checking whether this buffer has one or more tokens
304+
* or not.
305+
*
306+
* @return True if this buffer instance has no tokens
307+
*
308+
* @since 2.13
309+
*/
310+
public boolean isEmpty() {
311+
return (_appendAt == 0) && (_first == _last);
312+
}
302313
/*
303314
/**********************************************************
304315
/* Other custom methods not needed for implementing interfaces

src/test/java/com/fasterxml/jackson/databind/jsontype/ext/ExternalTypeIdTest.java

+37
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.fasterxml.jackson.databind.DeserializationFeature;
1111
import com.fasterxml.jackson.databind.MapperFeature;
1212
import com.fasterxml.jackson.databind.ObjectMapper;
13+
import com.fasterxml.jackson.databind.ObjectReader;
1314
import com.fasterxml.jackson.databind.json.JsonMapper;
1415

1516
// Tests for External type id, one that exists at same level as typed Object,
@@ -537,4 +538,40 @@ public void testBigDecimal965() throws Exception
537538
w.value.getClass().getSimpleName(), w.value.toString(), w2.value.getClass().getSimpleName(), w2.value.toString()),
538539
w.value.equals(w2.value));
539540
}
541+
542+
static class Box3008 {
543+
public String type;
544+
public Fruit3008 fruit;
545+
546+
public Box3008(@JsonProperty("type") String type,
547+
@JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "type")
548+
@JsonSubTypes({@JsonSubTypes.Type(value = Orange.class, name = "orange")})
549+
@JsonProperty("fruit")
550+
Fruit3008 fruit) {
551+
this.type = type;
552+
this.fruit = fruit;
553+
}
554+
}
555+
556+
// [databind#3008]: allow
557+
interface Fruit3008 {}
558+
559+
static class Orange implements Fruit3008 {
560+
public String name;
561+
public String color;
562+
563+
public Orange(@JsonProperty("name") String name, @JsonProperty("name") String color) {
564+
this.name = name;
565+
this.color = color;
566+
}
567+
}
568+
569+
// for [databind#3008]
570+
public void testIssue3008() throws Exception
571+
{
572+
ObjectReader r = MAPPER.readerFor(Box3008.class);
573+
Box3008 deserOrangeBox = r.readValue("{\"type\":null,\"fruit\":null}}");
574+
assertNull(deserOrangeBox.fruit);
575+
assertNull(deserOrangeBox.type); // error: "expected null, but was:<null>"
576+
}
540577
}

src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public void testBasicConfig() throws IOException
3737
assertSame(MAPPER, buf.getCodec());
3838
assertNotNull(buf.getOutputContext());
3939
assertFalse(buf.isClosed());
40+
assertTrue(buf.isEmpty());
4041

4142
buf.setCodec(null);
4243
assertNull(buf.getCodec());
@@ -57,7 +58,8 @@ public void testBasicConfig() throws IOException
5758
public void testSimpleWrites() throws IOException
5859
{
5960
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
60-
61+
assertTrue(buf.isEmpty());
62+
6163
// First, with empty buffer
6264
JsonParser p = buf.asParser();
6365
assertNull(p.currentToken());
@@ -66,6 +68,7 @@ public void testSimpleWrites() throws IOException
6668

6769
// Then with simple text
6870
buf.writeString("abc");
71+
assertFalse(buf.isEmpty());
6972

7073
p = buf.asParser();
7174
assertNull(p.currentToken());
@@ -657,6 +660,7 @@ public void testEmbeddedObjectCoerceCheck() throws Exception
657660
TokenBuffer buf = new TokenBuffer(null, false);
658661
Object inputPojo = new Sub1730();
659662
buf.writeEmbeddedObject(inputPojo);
663+
assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, buf.firstToken());
660664

661665
// first: raw value won't be transformed in any way:
662666
JsonParser p = buf.asParser();
@@ -666,4 +670,19 @@ public void testEmbeddedObjectCoerceCheck() throws Exception
666670
p.close();
667671
buf.close();
668672
}
673+
674+
public void testIsEmpty() throws Exception
675+
{
676+
// Let's check that segment boundary won't ruin it
677+
try (TokenBuffer buf = new TokenBuffer(null, false)) {
678+
assertTrue(buf.isEmpty());
679+
680+
for (int i = 0; i < 100; ++i) {
681+
buf.writeNumber(i);
682+
assertFalse(buf.isEmpty());
683+
}
684+
685+
assertEquals(JsonToken.VALUE_NUMBER_INT, buf.firstToken());
686+
}
687+
}
669688
}

0 commit comments

Comments
 (0)