Skip to content

Commit e75731b

Browse files
committed
Fix #1232
1 parent 98dd091 commit e75731b

File tree

10 files changed

+167
-26
lines changed

10 files changed

+167
-26
lines changed

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Project: jackson-databind
3535
#1217: `@JsonIgnoreProperties` on Pojo fields not working for deserialization
3636
(reported by Lokesh K)
3737
#1221: Use `Throwable.addSuppressed()` directly and/or via try-with-resources
38+
#1232: Add support for `JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES`
3839

3940
2.7.5 (not yet released)
4041

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ public BeanDeserializer(BeanDeserializerBase src, Set<String> ignorableProps) {
7272
super(src, ignorableProps);
7373
}
7474

75+
public BeanDeserializer(BeanDeserializerBase src, BeanPropertyMap props) {
76+
super(src, props);
77+
}
78+
7579
@Override
7680
public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
7781
{
@@ -98,12 +102,17 @@ public BeanDeserializer withIgnorableProperties(Set<String> ignorableProps) {
98102
return new BeanDeserializer(this, ignorableProps);
99103
}
100104

105+
@Override
106+
public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
107+
return new BeanDeserializer(this, props);
108+
}
109+
101110
@Override
102111
protected BeanDeserializerBase asArrayDeserializer() {
103112
SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
104113
return new BeanAsArrayDeserializer(this, props);
105114
}
106-
115+
107116
/*
108117
/**********************************************************
109118
/* JsonDeserializer implementation

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java

+56-2
Original file line numberDiff line numberDiff line change
@@ -385,13 +385,54 @@ public BeanDeserializerBase(BeanDeserializerBase src, Set<String> ignorableProps
385385
_beanProperties = src._beanProperties.withoutProperties(ignorableProps);
386386
}
387387

388+
/**
389+
* @since 2.8
390+
*/
391+
protected BeanDeserializerBase(BeanDeserializerBase src, BeanPropertyMap beanProps)
392+
{
393+
super(src._beanType);
394+
395+
_classAnnotations = src._classAnnotations;
396+
_beanType = src._beanType;
397+
398+
_valueInstantiator = src._valueInstantiator;
399+
_delegateDeserializer = src._delegateDeserializer;
400+
_propertyBasedCreator = src._propertyBasedCreator;
401+
402+
_beanProperties = beanProps;
403+
_backRefs = src._backRefs;
404+
_ignorableProps = src._ignorableProps;
405+
_ignoreAllUnknown = src._ignoreAllUnknown;
406+
_anySetter = src._anySetter;
407+
_injectables = src._injectables;
408+
_objectIdReader = src._objectIdReader;
409+
410+
_nonStandardCreation = src._nonStandardCreation;
411+
_unwrappedPropertyHandler = src._unwrappedPropertyHandler;
412+
_needViewProcesing = src._needViewProcesing;
413+
_serializationShape = src._serializationShape;
414+
415+
_vanillaProcessing = src._vanillaProcessing;
416+
}
417+
388418
@Override
389419
public abstract JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper);
390420

391421
public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir);
392422

393423
public abstract BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps);
394424

425+
/**
426+
* Mutant factory method that custom sub-classes must override; not left as
427+
* abstract to prevent more drastic backwards compatibility problems.
428+
*
429+
* @since 2.8
430+
*/
431+
public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
432+
throw new UnsupportedOperationException("Class "+getClass().getName()
433+
+" does not override `withBeanProperties()`, needs to");
434+
}
435+
395436
/**
396437
* Fluent factory for creating a variant that can handle
397438
* POJO output as a JSON Array. Implementations may ignore this request
@@ -690,9 +731,22 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
690731
// One more thing: are we asked to serialize POJO as array?
691732
JsonFormat.Value format = findFormatOverrides(ctxt, property, handledType());
692733
JsonFormat.Shape shape = null;
693-
if ((format != null) && format.hasShape()) {
694-
shape = format.getShape();
734+
if (format != null) {
735+
if (format.hasShape()) {
736+
shape = format.getShape();
737+
}
738+
// 16-May-2016, tatu: How about per-property case-insensitivity?
739+
Boolean B = format.getFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
740+
if (B != null) {
741+
// !!! TODO
742+
BeanPropertyMap propsOrig = _beanProperties;
743+
BeanPropertyMap props = propsOrig.withCaseInsensitivity(B.booleanValue());
744+
if (props != propsOrig) {
745+
contextual = contextual.withBeanProperties(props);
746+
}
747+
}
695748
}
749+
696750
if (shape == null) {
697751
shape = _serializationShape;
698752
}

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java

+19-7
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,23 @@ public class BeanDeserializerBuilder
2424
/**********************************************************
2525
*/
2626

27+
/**
28+
* Introspected information about POJO for deserializer to handle
29+
*/
2730
final protected BeanDescription _beanDesc;
2831

32+
/**
33+
* Whether default setting for properties without any view annotations
34+
* is to include (true) or exclude (false).
35+
*/
2936
final protected boolean _defaultViewInclusion;
30-
37+
38+
/**
39+
* Flag that indicates whether default settings suggest use of case-insensitive
40+
* property comparison or not.
41+
*/
3142
final protected boolean _caseInsensitivePropertyComparison;
32-
43+
3344
/*
3445
/**********************************************************
3546
/* Accumulated information about properties
@@ -63,6 +74,10 @@ public class BeanDeserializerBuilder
6374
*/
6475
protected ValueInstantiator _valueInstantiator;
6576

77+
/**
78+
* Handler for Object Id values, if Object Ids are enabled for the
79+
* bean type.
80+
*/
6681
protected ObjectIdReader _objectIdReader;
6782

6883
/**
@@ -88,7 +103,7 @@ public class BeanDeserializerBuilder
88103
* In addition, Builder may have additional configuration
89104
*/
90105
protected JsonPOJOBuilder.Value _builderConfig;
91-
106+
92107
/*
93108
/**********************************************************
94109
/* Life-cycle: construction
@@ -309,9 +324,6 @@ public JsonPOJOBuilder.Value getBuilderConfig() {
309324
/**
310325
* Method for constructing a {@link BeanDeserializer}, given all
311326
* information collected.
312-
*<p>
313-
* NOTE: Signature of this method did unfortunately change between Jackson 2.1
314-
* and Jackson 2.2
315327
*/
316328
public JsonDeserializer<?> build()
317329
{
@@ -342,7 +354,7 @@ public JsonDeserializer<?> build()
342354
ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED);
343355
propertyMap = propertyMap.withProperty(prop);
344356
}
345-
357+
346358
return new BeanDeserializer(this,
347359
_beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
348360
anyViews);

src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public BuilderBasedDeserializer(BuilderBasedDeserializer src, Set<String> ignora
8181
super(src, ignorableProps);
8282
_buildMethod = src._buildMethod;
8383
}
84+
85+
public BuilderBasedDeserializer(BuilderBasedDeserializer src, BeanPropertyMap props) {
86+
super(src, props);
87+
_buildMethod = src._buildMethod;
88+
}
8489

8590
@Override
8691
public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
@@ -93,17 +98,22 @@ public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper
9398
}
9499

95100
@Override
96-
public BuilderBasedDeserializer withObjectIdReader(ObjectIdReader oir) {
101+
public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
97102
return new BuilderBasedDeserializer(this, oir);
98103
}
99104

100105
@Override
101-
public BuilderBasedDeserializer withIgnorableProperties(Set<String> ignorableProps) {
106+
public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
102107
return new BuilderBasedDeserializer(this, ignorableProps);
103108
}
104109

105110
@Override
106-
protected BeanAsArrayBuilderDeserializer asArrayDeserializer() {
111+
public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
112+
return new BuilderBasedDeserializer(this, props);
113+
}
114+
115+
@Override
116+
protected BeanDeserializerBase asArrayDeserializer() {
107117
SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
108118
return new BeanAsArrayBuilderDeserializer(this, props, _buildMethod);
109119
}

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

+9-3
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,25 @@ public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper
5858
}
5959

6060
@Override
61-
public BeanAsArrayBuilderDeserializer withObjectIdReader(ObjectIdReader oir) {
61+
public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
6262
return new BeanAsArrayBuilderDeserializer(_delegate.withObjectIdReader(oir),
6363
_orderedProperties, _buildMethod);
6464
}
6565

6666
@Override
67-
public BeanAsArrayBuilderDeserializer withIgnorableProperties(Set<String> ignorableProps) {
67+
public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
6868
return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps),
6969
_orderedProperties, _buildMethod);
7070
}
7171

7272
@Override
73-
protected BeanAsArrayBuilderDeserializer asArrayDeserializer() {
73+
public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
74+
return new BeanAsArrayBuilderDeserializer(_delegate.withBeanProperties(props),
75+
_orderedProperties, _buildMethod);
76+
}
77+
78+
@Override
79+
protected BeanDeserializerBase asArrayDeserializer() {
7480
return this;
7581
}
7682

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,23 @@ public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper
6060
}
6161

6262
@Override
63-
public BeanAsArrayDeserializer withObjectIdReader(ObjectIdReader oir) {
63+
public BeanDeserializerBase withObjectIdReader(ObjectIdReader oir) {
6464
return new BeanAsArrayDeserializer(_delegate.withObjectIdReader(oir),
6565
_orderedProperties);
6666
}
6767

6868
@Override
69-
public BeanAsArrayDeserializer withIgnorableProperties(Set<String> ignorableProps) {
69+
public BeanDeserializerBase withIgnorableProperties(Set<String> ignorableProps) {
7070
return new BeanAsArrayDeserializer(_delegate.withIgnorableProperties(ignorableProps),
7171
_orderedProperties);
7272
}
7373

74+
@Override
75+
public BeanDeserializerBase withBeanProperties(BeanPropertyMap props) {
76+
return new BeanAsArrayDeserializer(_delegate.withBeanProperties(props),
77+
_orderedProperties);
78+
}
79+
7480
@Override
7581
protected BeanDeserializerBase asArrayDeserializer() {
7682
return this;

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

+29-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,34 @@ public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty>
6161
_propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
6262
init(props);
6363
}
64-
64+
65+
/**
66+
* @since 2.8
67+
*/
68+
protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)
69+
{
70+
_caseInsensitive = caseInsensitive;
71+
72+
// 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init
73+
// as well.
74+
_propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length);
75+
init(Arrays.asList(_propsInOrder));
76+
}
77+
78+
/**
79+
* Mutant factory method that constructs a new instance if desired case-insensitivity
80+
* state differs from the state of this instance; if states are the same, returns
81+
* <code>this</code>.
82+
*
83+
* @since 2.8
84+
*/
85+
public BeanPropertyMap withCaseInsensitivity(boolean state) {
86+
if (_caseInsensitive == state) {
87+
return this;
88+
}
89+
return new BeanPropertyMap(this, state);
90+
}
91+
6592
protected void init(Collection<SettableBeanProperty> props)
6693
{
6794
_size = props.size();
@@ -346,7 +373,7 @@ public SettableBeanProperty find(String key)
346373
if (_caseInsensitive) {
347374
key = key.toLowerCase();
348375
}
349-
376+
350377
// inlined `_hashCode(key)`
351378
int slot = key.hashCode() & _hashMask;
352379
// int h = key.hashCode();

src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeser.java renamed to src/test/java/com/fasterxml/jackson/databind/misc/CaseInsensitiveDeserTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import com.fasterxml.jackson.databind.*;
55

6-
public class CaseInsensitiveDeser extends BaseMapTest
6+
public class CaseInsensitiveDeserTest extends BaseMapTest
77
{
88
// [databind#1036]
99
static class BaseResponse {
@@ -45,7 +45,7 @@ static class Issue476Type {
4545
// [databind#566]
4646
public void testCaseInsensitiveDeserialization() throws Exception
4747
{
48-
final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}";
48+
final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}";
4949

5050
// first, verify default settings which do not accept improper case
5151
ObjectMapper mapper = new ObjectMapper();
@@ -62,7 +62,7 @@ public void testCaseInsensitiveDeserialization() throws Exception
6262

6363
// Definitely not OK to enable dynamically - the BeanPropertyMap (which is the consumer of this particular feature) gets cached.
6464
mapper = new ObjectMapper();
65-
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
65+
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
6666
ObjectReader r = mapper.readerFor(Issue476Bean.class);
6767
Issue476Bean result = r.readValue(JSON);
6868
assertEquals(result.value1.name, "fruit");

src/test/java/com/fasterxml/jackson/databind/struct/FormatFeaturesTest.java

+19-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static class WrapWriteWithArrays
2626

2727
public boolean[] bools = new boolean[] { true };
2828
}
29-
29+
3030
@JsonPropertyOrder( { "strings", "ints", "bools", "enums" })
3131
static class WrapWriteWithCollections
3232
{
@@ -89,6 +89,12 @@ static class Role {
8989
public String Name;
9090
}
9191

92+
static class CaseInsensitiveRoleWrapper
93+
{
94+
@JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES })
95+
public Role role;
96+
}
97+
9298
/*
9399
/**********************************************************
94100
/* Test methods, writing with single-element unwrapping
@@ -208,10 +214,20 @@ public void testSingleElementListRead() throws Exception {
208214
}
209215

210216
public void testSingleEnumSetRead() throws Exception {
211-
String json = aposToQuotes("{ 'values': 'B' }");
212-
EnumSetWrapper result = MAPPER.readValue(json, EnumSetWrapper.class);
217+
EnumSetWrapper result = MAPPER.readValue(aposToQuotes("{ 'values': 'B' }"),
218+
EnumSetWrapper.class);
213219
assertNotNull(result.values);
214220
assertEquals(1, result.values.size());
215221
assertEquals(ABC.B, result.values.iterator().next());
216222
}
223+
224+
// [databind#1232]: allow per-property case-insensitivity
225+
public void testCaseInsensitive() throws Exception {
226+
CaseInsensitiveRoleWrapper w = MAPPER.readValue
227+
(aposToQuotes("{'role':{'id':'12','name':'Foo'}}"),
228+
CaseInsensitiveRoleWrapper.class);
229+
assertNotNull(w);
230+
assertEquals("12", w.role.ID);
231+
assertEquals("Foo", w.role.Name);
232+
}
217233
}

0 commit comments

Comments
 (0)