1 package com.fasterxml.jackson.databind.misc; 2 3 import java.util.List; 4 5 import com.fasterxml.jackson.annotation.JsonCreator; 6 import com.fasterxml.jackson.annotation.JsonFormat; 7 import com.fasterxml.jackson.annotation.JsonProperty; 8 9 import com.fasterxml.jackson.core.JsonProcessingException; 10 11 import com.fasterxml.jackson.databind.*; 12 import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; 13 14 public class CaseInsensitiveDeserTest extends BaseMapTest 15 { 16 // [databind#1036] 17 static class BaseResponse { 18 public int errorCode; 19 public String debugMessage; 20 } 21 22 static class Issue476Bean { 23 public Issue476Type value1, value2; 24 } 25 static class Issue476Type { 26 public String name, value; 27 } 28 29 // [databind#1232]: allow per-property case-insensitivity 30 static class Role { 31 public String ID; 32 public String Name; 33 } 34 35 static class CaseInsensitiveRoleWrapper 36 { 37 @JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES }) 38 public Role role; 39 } 40 41 // [databind#1438] 42 static class InsensitiveCreator 43 { 44 int v; 45 46 @JsonCreator InsensitiveCreator(@sonProperty"value") int v0)47 public InsensitiveCreator(@JsonProperty("value") int v0) { 48 v = v0; 49 } 50 } 51 52 // [databind#1854] 53 static class Obj1854 { 54 private final int id; 55 56 private final List<ChildObj1854> items; 57 Obj1854(int id, List<ChildObj1854> items)58 public Obj1854(int id, List<ChildObj1854> items) { 59 this.id = id; 60 this.items = items; 61 } 62 63 @JsonCreator fromJson(@sonProperty"ID") int id, @JsonProperty("Items") List<ChildObj1854> items)64 public static Obj1854 fromJson(@JsonProperty("ID") int id, 65 @JsonProperty("Items") List<ChildObj1854> items) { 66 return new Obj1854(id, items); 67 } 68 getId()69 public int getId() { 70 return id; 71 } 72 getItems()73 public List<ChildObj1854> getItems() { 74 return items; 75 } 76 77 } 78 79 // [databind#1854] 80 static class ChildObj1854 { 81 private final String childId; 82 ChildObj1854(String id)83 private ChildObj1854(String id) { 84 this.childId = id; 85 } 86 87 @JsonCreator fromJson(@sonProperty"ChildID") String cid)88 public static ChildObj1854 fromJson(@JsonProperty("ChildID") String cid) { 89 return new ChildObj1854(cid); 90 } 91 getId()92 public String getId() { 93 return childId; 94 } 95 } 96 97 // [databind#1886]: allow case-insensitivity by default on a class 98 @JsonFormat(with={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES }) 99 static class CaseInsensitiveRole { 100 public String ID; 101 public String Name; 102 } 103 104 // [databind#1886]: allow case-insensitivity by default on a class 105 static class CaseInsensitiveRoleContainer { 106 public CaseInsensitiveRole role; 107 } 108 109 // [databind#1886]: ... but also overrides 110 static class CaseSensitiveRoleContainer { 111 @JsonFormat(without={ JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES }) 112 public CaseInsensitiveRole role; 113 } 114 115 /* 116 /******************************************************** 117 /* Test methods 118 /******************************************************** 119 */ 120 121 private final ObjectMapper MAPPER = newJsonMapper(); 122 private final ObjectMapper INSENSITIVE_MAPPER = jsonMapperBuilder() 123 .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) 124 .build(); 125 126 // [databind#566] testCaseInsensitiveDeserialization()127 public void testCaseInsensitiveDeserialization() throws Exception 128 { 129 final String JSON = "{\"Value1\" : {\"nAme\" : \"fruit\", \"vALUe\" : \"apple\"}, \"valUE2\" : {\"NAME\" : \"color\", \"value\" : \"red\"}}"; 130 131 // first, verify default settings which do not accept improper case 132 ObjectMapper mapper = new ObjectMapper(); 133 assertFalse(mapper.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); 134 try { 135 mapper.readValue(JSON, Issue476Bean.class); 136 137 fail("Should not accept improper case properties by default"); 138 } catch (JsonProcessingException e) { 139 verifyException(e, "Unrecognized field"); 140 assertValidLocation(e.getLocation()); 141 } 142 143 // Definitely not OK to enable dynamically - the BeanPropertyMap (which is the consumer of this particular feature) gets cached. 144 ObjectReader r = INSENSITIVE_MAPPER.readerFor(Issue476Bean.class); 145 Issue476Bean result = r.readValue(JSON); 146 assertEquals(result.value1.name, "fruit"); 147 assertEquals(result.value1.value, "apple"); 148 } 149 150 // [databind#1036] testCaseInsensitive1036()151 public void testCaseInsensitive1036() throws Exception 152 { 153 final String json = "{\"ErrorCode\":2,\"DebugMessage\":\"Signature not valid!\"}"; 154 // final String json = "{\"errorCode\":2,\"debugMessage\":\"Signature not valid!\"}"; 155 156 BaseResponse response = INSENSITIVE_MAPPER.readValue(json, BaseResponse.class); 157 assertEquals(2, response.errorCode); 158 assertEquals("Signature not valid!", response.debugMessage); 159 } 160 161 // [databind#1232]: allow per-property case-insensitivity testCaseInsensitiveWithFormat()162 public void testCaseInsensitiveWithFormat() throws Exception { 163 CaseInsensitiveRoleWrapper w = MAPPER.readValue 164 (aposToQuotes("{'role':{'id':'12','name':'Foo'}}"), 165 CaseInsensitiveRoleWrapper.class); 166 assertNotNull(w); 167 assertEquals("12", w.role.ID); 168 assertEquals("Foo", w.role.Name); 169 } 170 171 // [databind#1438] testCreatorWithInsensitive()172 public void testCreatorWithInsensitive() throws Exception 173 { 174 final String json = aposToQuotes("{'VALUE':3}"); 175 InsensitiveCreator bean = INSENSITIVE_MAPPER.readValue(json, InsensitiveCreator.class); 176 assertEquals(3, bean.v); 177 } 178 179 // And allow config overrides too testCaseInsensitiveViaConfigOverride()180 public void testCaseInsensitiveViaConfigOverride() throws Exception 181 { 182 ObjectMapper mapper = new ObjectMapper(); 183 mapper.configOverride(Role.class) 184 .setFormat(JsonFormat.Value.empty() 185 .withFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); 186 Role role = mapper.readValue 187 (aposToQuotes("{'id':'12','name':'Foo'}"), 188 Role.class); 189 assertNotNull(role); 190 assertEquals("12", role.ID); 191 assertEquals("Foo", role.Name); 192 } 193 testIssue1854()194 public void testIssue1854() throws Exception 195 { 196 final String DOC = aposToQuotes("{'ID': 1, 'Items': [ { 'ChildID': 10 } ]}"); 197 Obj1854 result = INSENSITIVE_MAPPER.readValue(DOC, Obj1854.class); 198 assertNotNull(result); 199 assertEquals(1, result.getId()); 200 assertNotNull(result.getItems()); 201 assertEquals(1, result.getItems().size()); 202 } 203 204 205 // [databind#1886]: allow case-insensitivity by default on a class testCaseInsensitiveViaClassAnnotation()206 public void testCaseInsensitiveViaClassAnnotation() throws Exception 207 { 208 final String CONTAINED = aposToQuotes("{'role': {'id':'3','name':'Bob'}}"); 209 210 // First: via wrapper/container: 211 CaseInsensitiveRoleContainer cont = MAPPER.readValue(CONTAINED, 212 CaseInsensitiveRoleContainer.class); 213 assertEquals("3", cont.role.ID); 214 assertEquals("Bob", cont.role.Name); 215 216 // second: directly as root value 217 CaseInsensitiveRole role = MAPPER.readValue 218 (aposToQuotes("{'id':'12','name':'Billy'}"), 219 CaseInsensitiveRole.class); 220 assertEquals("12", role.ID); 221 assertEquals("Billy", role.Name); 222 223 // and finally, more complicated; should be possible to force sensitivity: 224 try { 225 /*CaseSensitiveRoleContainer r =*/ MAPPER.readValue(CONTAINED, 226 CaseSensitiveRoleContainer.class); 227 fail("Should not pass"); 228 } catch (UnrecognizedPropertyException e) { 229 verifyException(e, "Unrecognized "); 230 verifyException(e, "\"id\""); 231 } 232 } 233 } 234