Skip to content

Commit 5aece76

Browse files
committed
Fixed #1036
1 parent 3be8f74 commit 5aece76

File tree

6 files changed

+143
-45
lines changed

6 files changed

+143
-45
lines changed

release-notes/CREDITS

+4
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,7 @@ Jayson Minard (apatrida@github)
354354
David Bakin (david-bakin@github)
355355
* Reported #1013: `@JsonUnwrapped` is not treated as assuming `@JsonProperty("")`
356356
(2.6.4)
357+
358+
Dmitry Romantsov (DmRomantsov@github)
359+
* Reported #1036: Problem with case-insensitive deserialization
360+
(2.6.4)

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Project: jackson-databind
1616
(reported by Jayson M)
1717
#1013: `@JsonUnwrapped` is not treated as assuming `@JsonProperty("")`
1818
(reported by David B)
19+
#1036: Problem with case-insensitive deserialization
20+
(repoted by Dmitry R)
1921
- Fix a minor problem with `@JsonNaming` not recognizing default value
2022

2123
2.6.3 (12-Oct-2015)

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -451,14 +451,14 @@ public void resolve(DeserializationContext ctxt)
451451
}
452452
}
453453

454-
// [JACKSON-235]: need to link managed references with matching back references
454+
// Need to link managed references with matching back references
455455
prop = _resolveManagedReferenceProperty(ctxt, prop);
456456

457-
// issue #351: need to wrap properties that require object id resolution.
457+
// [databind#351[: need to wrap properties that require object id resolution.
458458
if (!(prop instanceof ManagedReferenceProperty)) {
459459
prop = _resolvedObjectIdProperty(ctxt, prop);
460460
}
461-
// [JACKSON-132]: support unwrapped values (via @JsonUnwrapped)
461+
// Support unwrapped values (via @JsonUnwrapped)
462462
SettableBeanProperty u = _resolveUnwrappedProperty(ctxt, prop);
463463
if (u != null) {
464464
prop = u;
@@ -472,7 +472,7 @@ public void resolve(DeserializationContext ctxt)
472472
_beanProperties.remove(prop);
473473
continue;
474474
}
475-
// [JACKSON-594]: non-static inner classes too:
475+
// non-static inner classes too:
476476
prop = _resolveInnerClassValuedProperty(ctxt, prop);
477477
if (prop != origProp) {
478478
_beanProperties.replace(prop);

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

+46-10
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,17 @@ public BeanPropertyMap renameAll(NameTransformer transformer)
242242
public void replace(SettableBeanProperty newProp)
243243
{
244244
String key = getPropertyName(newProp);
245-
for (int i = 1, end = _hashArea.length; i < end; i += 2) {
246-
SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
247-
if ((prop != null) && prop.getName().equals(key)) {
248-
_hashArea[i] = newProp;
249-
// also, replace in in-order
250-
_propsInOrder[_findFromOrdered(prop)] = newProp;
251-
return;
252-
}
245+
int ix = _findIndexInHash(key);
246+
247+
if (ix >= 0) {
248+
SettableBeanProperty prop = (SettableBeanProperty) _hashArea[ix];
249+
_hashArea[ix] = newProp;
250+
// also, replace in in-order
251+
_propsInOrder[_findFromOrdered(prop)] = newProp;
252+
return;
253253
}
254-
throw new NoSuchElementException("No entry '"+newProp.getName()+"' found, can't replace");
254+
255+
throw new NoSuchElementException("No entry '"+key+"' found, can't replace");
255256
}
256257

257258
private List<SettableBeanProperty> properties() {
@@ -296,6 +297,8 @@ protected final String getPropertyName(SettableBeanProperty prop) {
296297
*/
297298
public SettableBeanProperty find(int index)
298299
{
300+
// note: will scan the whole area, including primary, secondary and
301+
// possible spill-area
299302
for (int i = 1, end = _hashArea.length; i < end; i += 2) {
300303
SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
301304
if ((prop != null) && (index == prop.getPropertyIndex())) {
@@ -486,7 +489,40 @@ protected void wrapAndThrow(Throwable t, Object bean, String fieldName, Deserial
486489
throw JsonMappingException.wrapWithPath(t, bean, fieldName);
487490
}
488491

489-
private int _findFromOrdered(SettableBeanProperty prop) {
492+
/**
493+
* Helper method used to find exact location of a property with name
494+
* given exactly, not subject to case changes, within hash area.
495+
* Expectation is that such property SHOULD exist, although no
496+
* exception is thrown.
497+
*
498+
* @since 2.7
499+
*/
500+
private final int _findIndexInHash(String key)
501+
{
502+
final int slot = _hashCode(key);
503+
int ix = (slot<<1);
504+
505+
// primary match?
506+
if (key.equals(_hashArea[ix])) {
507+
return ix+1;
508+
}
509+
// no? secondary?
510+
int hashSize = _hashMask+1;
511+
ix = hashSize + (slot>>1) << 1;
512+
if (key.equals(_hashArea[ix])) {
513+
return ix+1;
514+
}
515+
// perhaps spill then
516+
int i = (hashSize + (hashSize>>1)) << 1;
517+
for (int end = i + _spillCount; i < end; i += 2) {
518+
if (key.equals(_hashArea[i])) {
519+
return i+1;
520+
}
521+
}
522+
return -1;
523+
}
524+
525+
private final int _findFromOrdered(SettableBeanProperty prop) {
490526
for (int i = 0, end = _propsInOrder.length; i < end; ++i) {
491527
if (_propsInOrder[i] == prop) {
492528
return i;

src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java

+3-31
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public Object deserialize(JsonParser jp, DeserializationContext ctxt)
9898
return new Bean(a, b);
9999
}
100100
}
101-
102101
static class Issue476Bean {
103102
public Issue476Type value1, value2;
104103
}
@@ -237,12 +236,12 @@ public String deserialize(JsonParser p, DeserializationContext ctxt)
237236

238237
/*
239238
/********************************************************
240-
/* Unit tests
239+
/* Test methods
241240
/********************************************************
242241
*/
243242

244243
private final ObjectMapper MAPPER = new ObjectMapper();
245-
244+
246245
public void testPropertyRemoval() throws Exception
247246
{
248247
ObjectMapper mapper = new ObjectMapper();
@@ -315,34 +314,7 @@ public void testPOJOFromEmptyArray() throws Exception
315314
assertNull(result);
316315
}
317316

318-
// [Databind#566]
319-
public void testCaseInsensitiveDeserialization() throws Exception
320-
{
321-
final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}";
322-
323-
// first, verify default settings which do not accept improper case
324-
ObjectMapper mapper = new ObjectMapper();
325-
assertFalse(mapper.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
326-
327-
try {
328-
mapper.readValue(JSON, Issue476Bean.class);
329-
330-
fail("Should not accept improper case properties by default");
331-
} catch (JsonProcessingException e) {
332-
verifyException(e, "Unrecognized field");
333-
assertValidLocation(e.getLocation());
334-
}
335-
336-
// Definitely not OK to enable dynamically - the BeanPropertyMap (which is the consumer of this particular feature) gets cached.
337-
mapper = new ObjectMapper();
338-
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
339-
ObjectReader r = mapper.readerFor(Issue476Bean.class);
340-
Issue476Bean result = r.readValue(JSON);
341-
assertEquals(result.value1.name, "fruit");
342-
assertEquals(result.value1.value, "apple");
343-
}
344-
345-
// [Issue#120]
317+
// [databind#120]
346318
public void testModifyArrayDeserializer() throws Exception
347319
{
348320
ObjectMapper mapper = new ObjectMapper();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.fasterxml.jackson.databind.misc;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.*;
5+
6+
public class CaseInsensitiveDeser extends BaseMapTest
7+
{
8+
// [databind#1036]
9+
static class BaseResponse {
10+
public int errorCode;
11+
public String debugMessage;
12+
13+
/*
14+
public String getDebugMessage() {
15+
return debugMessage;
16+
}
17+
18+
public void setDebugMessage(String debugMessage) {
19+
this.debugMessage = debugMessage;
20+
}
21+
22+
public int getErrorCode() {
23+
return errorCode;
24+
}
25+
26+
public void setErrorCode(int errorCode) {
27+
this.errorCode = errorCode;
28+
}
29+
*/
30+
}
31+
32+
static class Issue476Bean {
33+
public Issue476Type value1, value2;
34+
}
35+
static class Issue476Type {
36+
public String name, value;
37+
}
38+
39+
/*
40+
/********************************************************
41+
/* Test methods
42+
/********************************************************
43+
*/
44+
45+
// [databind#566]
46+
public void testCaseInsensitiveDeserialization() throws Exception
47+
{
48+
final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}";
49+
50+
// first, verify default settings which do not accept improper case
51+
ObjectMapper mapper = new ObjectMapper();
52+
assertFalse(mapper.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES));
53+
54+
try {
55+
mapper.readValue(JSON, Issue476Bean.class);
56+
57+
fail("Should not accept improper case properties by default");
58+
} catch (JsonProcessingException e) {
59+
verifyException(e, "Unrecognized field");
60+
assertValidLocation(e.getLocation());
61+
}
62+
63+
// Definitely not OK to enable dynamically - the BeanPropertyMap (which is the consumer of this particular feature) gets cached.
64+
mapper = new ObjectMapper();
65+
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
66+
ObjectReader r = mapper.readerFor(Issue476Bean.class);
67+
Issue476Bean result = r.readValue(JSON);
68+
assertEquals(result.value1.name, "fruit");
69+
assertEquals(result.value1.value, "apple");
70+
}
71+
72+
// [databind#1036]
73+
public void testCaseInsensitive1036() throws Exception
74+
{
75+
final String json = "{\"ErrorCode\":2,\"DebugMessage\":\"Signature not valid!\"}";
76+
// final String json = "{\"errorCode\":2,\"debugMessage\":\"Signature not valid!\"}";
77+
78+
ObjectMapper mapper = new ObjectMapper();
79+
mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
80+
BaseResponse response = mapper.readValue(json, BaseResponse.class);
81+
assertEquals(2, response.errorCode);
82+
assertEquals("Signature not valid!", response.debugMessage);
83+
}
84+
}

0 commit comments

Comments
 (0)