Skip to content

Commit 2eafcdf

Browse files
committed
Backport #945/#944 into 2.6
1 parent 812d675 commit 2eafcdf

14 files changed

+362
-8
lines changed

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ javax.xml.datatype, javax.xml.namespace, javax.xml.parsers
101101
<version>0.9.2</version>
102102
<scope>test</scope>
103103
</dependency>
104+
<dependency>
105+
<groupId>com.google.guava</groupId>
106+
<artifactId>guava</artifactId>
107+
<version>18.0</version>
108+
<scope>test</scope>
109+
</dependency>
104110
</dependencies>
105111

106112
<build>

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

+31-8
Original file line numberDiff line numberDiff line change
@@ -1387,16 +1387,23 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
13871387
throws JsonMappingException
13881388
{
13891389
final DeserializationConfig config = ctxt.getConfig();
1390+
Class<?> enumClass = type.getRawClass();
1391+
13901392
BeanDescription beanDesc = config.introspect(type);
1391-
JsonDeserializer<?> des = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
1393+
// 24-Sep-2015, bim: a key deserializer is the preferred thing.
1394+
KeyDeserializer des = findKeyDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
13921395
if (des != null) {
1393-
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, des);
1394-
}
1395-
Class<?> enumClass = type.getRawClass();
1396-
// 23-Nov-2010, tatu: Custom deserializer?
1397-
JsonDeserializer<?> custom = _findCustomEnumDeserializer(enumClass, config, beanDesc);
1398-
if (custom != null) {
1399-
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, custom);
1396+
return des;
1397+
} else {
1398+
// 24-Sep-2015, bim: if no key deser, look for enum deserializer first, then a plain deser.
1399+
JsonDeserializer<?> custom = _findCustomEnumDeserializer(enumClass, config, beanDesc);
1400+
if (custom != null) {
1401+
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, custom);
1402+
}
1403+
JsonDeserializer<?> valueDesForKey = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
1404+
if (valueDesForKey != null) {
1405+
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, valueDesForKey);
1406+
}
14001407
}
14011408

14021409
EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
@@ -1728,6 +1735,22 @@ protected JsonDeserializer<Object> findDeserializerFromAnnotation(Deserializatio
17281735
return ctxt.deserializerInstance(ann, deserDef);
17291736
}
17301737

1738+
/**
1739+
* Helper method called to check if a class or method
1740+
* has annotation that tells which class to use for deserialization.
1741+
* Returns null if no such annotation found.
1742+
*/
1743+
protected KeyDeserializer findKeyDeserializerFromAnnotation(DeserializationContext ctxt,
1744+
Annotated ann)
1745+
throws JsonMappingException
1746+
{
1747+
Object deserDef = ctxt.getAnnotationIntrospector().findKeyDeserializer(ann);
1748+
if (deserDef == null) {
1749+
return null;
1750+
}
1751+
return ctxt.keyDeserializerInstance(ann, deserDef);
1752+
}
1753+
17311754
/**
17321755
* Method called to see if given method has annotations that indicate
17331756
* a more specific type than what the argument specifies.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
package com.fasterxml.jackson.databind.module;
3+
4+
import com.fasterxml.jackson.core.type.TypeReference;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.module.customenumkey.KeyEnum;
8+
import com.fasterxml.jackson.databind.module.customenumkey.TestEnum;
9+
import com.fasterxml.jackson.databind.module.customenumkey.TestEnumModule;
10+
import com.fasterxml.jackson.databind.node.ObjectNode;
11+
import com.google.common.collect.Maps;
12+
import com.google.common.io.Resources;
13+
import org.junit.Ignore;
14+
import org.junit.Test;
15+
16+
import java.io.File;
17+
import java.io.IOException;
18+
import java.util.Map;
19+
import java.util.Set;
20+
21+
import static junit.framework.TestCase.assertNotNull;
22+
import static org.junit.Assert.assertEquals;
23+
24+
/**
25+
*
26+
*/
27+
public class TestCustomKeyDeserializer {
28+
@Test
29+
public void troubleWithKeys() throws Exception {
30+
ObjectMapper plainObjectMapper = new ObjectMapper();
31+
JsonNode tree = plainObjectMapper.readTree(Resources.getResource("data/enum-custom-key-test.json"));
32+
ObjectMapper fancyObjectMapper = TestEnumModule.setupObjectMapper(new ObjectMapper());
33+
// this line is might throw with Jackson 2.6.2.
34+
Map<TestEnum, Set<String>> map = fancyObjectMapper.convertValue(tree, new TypeReference<Map<TestEnum, Set<String>>>() {
35+
});
36+
assertNotNull(map);
37+
}
38+
39+
@Ignore("issue 749, more or less")
40+
@Test
41+
public void tree() throws Exception {
42+
43+
Map<KeyEnum, Object> inputMap = Maps.newHashMap();
44+
Map<TestEnum, Map<String, String>> replacements = Maps.newHashMap();
45+
Map<String, String> reps = Maps.newHashMap();
46+
reps.put("1", "one");
47+
replacements.put(TestEnum.GREEN, reps);
48+
inputMap.put(KeyEnum.replacements, replacements);
49+
ObjectMapper mapper = TestEnumModule.setupObjectMapper(new ObjectMapper());
50+
JsonNode tree = mapper.valueToTree(inputMap);
51+
ObjectNode ob = (ObjectNode) tree;
52+
JsonNode inner = ob.get("replacements");
53+
String firstFieldName = inner.fieldNames().next();
54+
assertEquals("green", firstFieldName);
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
3+
package com.fasterxml.jackson.databind.module.customenumkey;
4+
5+
import java.io.File;
6+
import java.util.Map;
7+
8+
/**
9+
*
10+
*/
11+
public class Bean {
12+
private File rootDirectory;
13+
private String licenseString;
14+
private Map<TestEnum, Map<String, String>> replacements;
15+
16+
public File getRootDirectory() {
17+
return rootDirectory;
18+
}
19+
20+
public void setRootDirectory(File rootDirectory) {
21+
this.rootDirectory = rootDirectory;
22+
}
23+
24+
public String getLicenseString() {
25+
return licenseString;
26+
}
27+
28+
public void setLicenseString(String licenseString) {
29+
this.licenseString = licenseString;
30+
}
31+
32+
public Map<TestEnum, Map<String, String>> getReplacements() {
33+
return replacements;
34+
}
35+
36+
public void setReplacements(Map<TestEnum, Map<String, String>> replacements) {
37+
this.replacements = replacements;
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
package com.fasterxml.jackson.databind.module.customenumkey;
3+
4+
/**
5+
*/
6+
public enum KeyEnum {
7+
replacements,
8+
rootDirectory,
9+
licenseString
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/******************************************************************************
2+
** This data and information is proprietary to, and a valuable trade secret
3+
** of, Basis Technology Corp. It is given in confidence by Basis Technology
4+
** and may only be used as permitted under the license agreement under which
5+
** it has been distributed, and in no other way.
6+
**
7+
** Copyright (c) 2014 Basis Technology Corporation All rights reserved.
8+
**
9+
** The technical data and information provided herein are provided with
10+
** `limited rights', and the computer software provided herein is provided
11+
** with `restricted rights' as those terms are defined in DAR and ASPR
12+
** 7-104.9(a).
13+
******************************************************************************/
14+
15+
package com.fasterxml.jackson.databind.module.customenumkey;
16+
17+
import com.fasterxml.jackson.core.Version;
18+
19+
/**
20+
* Common class to set up Jackson version from property.
21+
*/
22+
final class ModuleVersion {
23+
static final Version VERSION = new Version(0, 0, 0, "", "", "");
24+
25+
private ModuleVersion() {
26+
//
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/******************************************************************************
2+
* * This data and information is proprietary to, and a valuable trade secret
3+
* * of, Basis Technology Corp. It is given in confidence by Basis Technology
4+
* * and may only be used as permitted under the license agreement under which
5+
* * it has been distributed, and in no other way.
6+
* *
7+
* * Copyright (c) 2015 Basis Technology Corporation All rights reserved.
8+
* *
9+
* * The technical data and information provided herein are provided with
10+
* * `limited rights', and the computer software provided herein is provided
11+
* * with `restricted rights' as those terms are defined in DAR and ASPR
12+
* * 7-104.9(a).
13+
******************************************************************************/
14+
15+
package com.fasterxml.jackson.databind.module.customenumkey;
16+
17+
/**
18+
*
19+
*/
20+
public enum TestEnum {
21+
RED("red"),
22+
GREEN("green");
23+
24+
private final String code;
25+
26+
TestEnum(String code) {
27+
this.code = code;
28+
}
29+
30+
public static TestEnum lookup(String lower) {
31+
for (TestEnum item : values()) {
32+
if (item.code().equals(lower)) {
33+
return item;
34+
}
35+
}
36+
throw new IllegalArgumentException("Invalid code " + lower);
37+
}
38+
39+
public String code() {
40+
return code;
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
package com.fasterxml.jackson.databind.module.customenumkey;
3+
4+
import com.fasterxml.jackson.core.JsonParser;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
7+
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
8+
9+
import java.io.IOException;
10+
11+
/**
12+
*
13+
*/
14+
public class TestEnumDeserializer extends StdDeserializer<TestEnum> {
15+
16+
public TestEnumDeserializer() {
17+
super(TestEnum.class);
18+
}
19+
20+
@Override
21+
public TestEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
22+
String code = jp.getText();
23+
try {
24+
return TestEnum.lookup(code);
25+
} catch (IllegalArgumentException e) {
26+
throw new InvalidFormatException("Undefined ISO-639 language code", jp.getCurrentLocation(), code, TestEnum.class);
27+
}
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
package com.fasterxml.jackson.databind.module.customenumkey;
3+
4+
import com.fasterxml.jackson.databind.DeserializationContext;
5+
import com.fasterxml.jackson.databind.KeyDeserializer;
6+
7+
import java.io.IOException;
8+
9+
/**
10+
*
11+
*/
12+
public class TestEnumKeyDeserializer extends KeyDeserializer {
13+
@Override
14+
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
15+
try {
16+
return TestEnum.lookup(key);
17+
} catch (IllegalArgumentException e) {
18+
throw ctxt.weirdKeyException(TestEnum.class, key, "Unknown code");
19+
}
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
package com.fasterxml.jackson.databind.module.customenumkey;
3+
4+
import com.fasterxml.jackson.core.JsonGenerator;
5+
import com.fasterxml.jackson.databind.JsonSerializer;
6+
import com.fasterxml.jackson.databind.SerializerProvider;
7+
8+
import java.io.IOException;
9+
10+
/**
11+
* Jackson serializer for LanguageCode used as a key.
12+
*/
13+
public class TestEnumKeySerializer extends JsonSerializer<TestEnum> {
14+
@Override
15+
public void serialize(TestEnum test, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
16+
jsonGenerator.writeFieldName(test.code());
17+
}
18+
19+
@Override
20+
public Class<TestEnum> handledType() {
21+
return TestEnum.class;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
package com.fasterxml.jackson.databind.module.customenumkey;
4+
5+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
6+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
7+
8+
@JsonSerialize(using = TestEnumSerializer.class, keyUsing = TestEnumKeySerializer.class)
9+
@JsonDeserialize(using = TestEnumDeserializer.class, keyUsing = TestEnumKeyDeserializer.class)
10+
public enum TestEnumMixin {
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
3+
package com.fasterxml.jackson.databind.module.customenumkey;
4+
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.fasterxml.jackson.databind.module.SimpleModule;
7+
import com.fasterxml.jackson.databind.module.SimpleSerializers;
8+
9+
public class TestEnumModule extends SimpleModule {
10+
11+
public TestEnumModule() {
12+
super(ModuleVersion.VERSION);
13+
}
14+
15+
public void setupModule(SetupContext context) {
16+
context.setMixInAnnotations(TestEnum.class, TestEnumMixin.class);
17+
SimpleSerializers keySerializers = new SimpleSerializers();
18+
keySerializers.addSerializer(new TestEnumKeySerializer());
19+
context.addKeySerializers(keySerializers);
20+
}
21+
22+
public static ObjectMapper setupObjectMapper(ObjectMapper mapper) {
23+
final TestEnumModule module = new TestEnumModule();
24+
mapper.registerModule(module);
25+
return mapper;
26+
}
27+
}

0 commit comments

Comments
 (0)