Skip to content

Commit 707db7a

Browse files
committed
Fix #677
1 parent 44dea1f commit 707db7a

File tree

3 files changed

+71
-28
lines changed

3 files changed

+71
-28
lines changed

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Project: jackson-databind
2222
#664: Add `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to prevent coercion of floating point
2323
numbers int `int`/`long`/`Integer`/`Long`
2424
(requested by wenzis@github)
25+
#677: Specifying `Enum` value serialization using `@JsonProperty`
26+
(requested by Allen C, allenchen1154@github)
2527
#679: Add `isEmpty()` implementation for `JsonNode` serializers
2628
#688: Provide a means for an ObjectMapper to discover mixin annotation classes on demand
2729
(requested by Laird N)

src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.fasterxml.jackson.databind.introspect;
22

33
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.Field;
45
import java.util.*;
56

67
import com.fasterxml.jackson.annotation.*;
@@ -57,8 +58,31 @@ public boolean isAnnotationBundle(Annotation ann) {
5758
/**********************************************************
5859
*/
5960

60-
// default impl is fine:
61-
//public String findEnumValue(Enum<?> value) { return value.name(); }
61+
/**
62+
* Since 2.6, we have supported use of {@link JsonProperty} for specifying
63+
* explicit serialized name
64+
*/
65+
@Override
66+
public String findEnumValue(Enum<?> value)
67+
{
68+
// 11-Jun-2015, tatu: As per [databind#677], need to allow explicit naming.
69+
// Unfortunately can not quite use standard AnnotatedClass here (due to various
70+
// reasons, including odd representation JVM uses); has to do for now
71+
try {
72+
// We know that values are actually static fields with matching name so:
73+
Field f = value.getClass().getField(value.name());
74+
if (f != null) {
75+
JsonProperty prop = f.getAnnotation(JsonProperty.class);
76+
String n = prop.value();
77+
if (n != null && !n.isEmpty()) {
78+
return n;
79+
}
80+
}
81+
} catch (Exception e) {
82+
// no such field, or access; neither which we can do much about
83+
}
84+
return value.name();
85+
}
6286

6387
/*
6488
/**********************************************************

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

+43-26
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ private LowerCaseEnum() { }
8181
public String toString() { return name().toLowerCase(); }
8282
}
8383

84-
// for [JACKSON-749]
8584
protected enum EnumWithJsonValue {
8685
A("foo"), B("bar");
8786
private final String name;
@@ -92,13 +91,11 @@ private EnumWithJsonValue(String n) {
9291
@Override
9392
public String toString() { return name; }
9493
}
95-
96-
// [JACKSON-810]
94+
9795
static class ClassWithEnumMapKey {
98-
@JsonProperty Map<TestEnum, String> map;
96+
@JsonProperty Map<TestEnum, String> map;
9997
}
10098

101-
// [JACKSON-834]
10299
protected enum TestEnumFor834
103100
{
104101
ENUM_A(1), ENUM_B(2), ENUM_C(3);
@@ -127,7 +124,7 @@ protected enum TestEnum324
127124
}
128125
}
129126

130-
// [Issue#745]
127+
// [databind#745]
131128
static class DelegatingDeserializers extends Deserializers.Base
132129
{
133130
@Override
@@ -145,7 +142,7 @@ public JsonDeserializer<?> findEnumDeserializer(final Class<?> type, final Deser
145142
}
146143
}
147144

148-
// [Issue#745]
145+
// [databind#745]
149146
static class DelegatingDeserializersModule extends SimpleModule
150147
{
151148
@Override
@@ -154,14 +151,29 @@ public void setupModule(final SetupContext context) {
154151
}
155152
}
156153

154+
// [databind#677]
155+
static enum EnumWithPropertyAnno {
156+
@JsonProperty("a")
157+
A,
158+
159+
// For this value, force use of anonymous sub-class, to ensure things still work
160+
@JsonProperty("b")
161+
B {
162+
@Override
163+
public String toString() {
164+
return "bb";
165+
}
166+
}
167+
;
168+
}
169+
157170
/*
158171
/**********************************************************
159172
/* Tests
160173
/**********************************************************
161174
*/
162175

163176
protected final ObjectMapper MAPPER = new ObjectMapper();
164-
165177

166178
public void testSimple() throws Exception
167179
{
@@ -226,15 +238,13 @@ public void testEnumMaps() throws Exception
226238
new TypeReference<EnumMap<TestEnum,String>>() { });
227239
assertEquals("value", value.get(TestEnum.OK));
228240
}
229-
230-
// Test [JACKSON-214]
241+
231242
public void testSubclassedEnums() throws Exception
232243
{
233244
EnumWithSubClass value = MAPPER.readValue("\"A\"", EnumWithSubClass.class);
234245
assertEquals(EnumWithSubClass.A, value);
235246
}
236247

237-
// [JACKSON-193]
238248
public void testCreatorEnums() throws Exception {
239249
EnumWithCreator value = MAPPER.readValue("\"enumA\"", EnumWithCreator.class);
240250
assertEquals(EnumWithCreator.A, value);
@@ -244,8 +254,7 @@ public void testCreatorEnumsFromBigDecimal() throws Exception {
244254
EnumWithBDCreator value = MAPPER.readValue("\"8.0\"", EnumWithBDCreator.class);
245255
assertEquals(EnumWithBDCreator.E8, value);
246256
}
247-
248-
// [JACKSON-212]
257+
249258
public void testToStringEnums() throws Exception
250259
{
251260
// can't reuse global one due to reconfig
@@ -255,7 +264,6 @@ public void testToStringEnums() throws Exception
255264
assertEquals(LowerCaseEnum.C, value);
256265
}
257266

258-
// [JACKSON-212]
259267
public void testToStringEnumMaps() throws Exception
260268
{
261269
// can't reuse global one due to reconfig
@@ -266,7 +274,6 @@ public void testToStringEnumMaps() throws Exception
266274
assertEquals("value", value.get(LowerCaseEnum.A));
267275
}
268276

269-
// [JACKSON-412], disallow use of numbers
270277
public void testNumbersToEnums() throws Exception
271278
{
272279
// by default numbers are fine:
@@ -293,7 +300,6 @@ public void testNumbersToEnums() throws Exception
293300
}
294301
}
295302

296-
// [JACKSON-684], enums using index
297303
public void testEnumsWithIndex() throws Exception
298304
{
299305
ObjectMapper m = new ObjectMapper();
@@ -303,8 +309,7 @@ public void testEnumsWithIndex() throws Exception
303309
TestEnum result = m.readValue(json, TestEnum.class);
304310
assertSame(TestEnum.RULES, result);
305311
}
306-
307-
// [JACKSON-749]: @JsonValue should be considered as well
312+
308313
public void testEnumsWithJsonValue() throws Exception
309314
{
310315
// first, enum as is
@@ -329,8 +334,6 @@ public void testEnumsWithJsonValue() throws Exception
329334
assertEquals(Integer.valueOf(13), map.get(EnumWithJsonValue.A));
330335
}
331336

332-
// [JACKSON-756], next three tests
333-
334337
public void testEnumWithCreatorEnumMaps() throws Exception {
335338
EnumMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
336339
new TypeReference<EnumMap<EnumWithCreator,String>>() {});
@@ -386,15 +389,14 @@ public void testDoNotAllowUnknownEnumValuesAsMapKeysWhenReadAsNullDisabled() thr
386389
}
387390
}
388391

389-
// [JACKSON-834]
390392
public void testEnumsFromInts() throws Exception
391393
{
392394
Object ob = MAPPER.readValue("1 ", TestEnumFor834.class);
393395
assertEquals(TestEnumFor834.class, ob.getClass());
394396
assertSame(TestEnumFor834.ENUM_A, ob);
395397
}
396398

397-
// [Issue#141]: allow mapping of empty String into null
399+
// [databind#141]: allow mapping of empty String into null
398400
public void testEnumsWithEmpty() throws Exception
399401
{
400402
final ObjectMapper mapper = new ObjectMapper();
@@ -413,7 +415,7 @@ public void testGenericEnumDeserialization() throws Exception
413415
assertEquals(TestEnum.JACKSON, mapper.readValue(quote("jackson"), TestEnum.class));
414416
}
415417

416-
// [Issue#324]
418+
// [databind#324]
417419
public void testExceptionFromCreator() throws Exception
418420
{
419421
try {
@@ -424,7 +426,7 @@ public void testExceptionFromCreator() throws Exception
424426
}
425427
}
426428

427-
// [Issue#381]
429+
// [databind#381]
428430
public void testUnwrappedEnum() throws Exception {
429431
final ObjectMapper mapper = new ObjectMapper();
430432
mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
@@ -443,7 +445,7 @@ public void testUnwrappedEnumException() throws Exception {
443445
}
444446
}
445447

446-
// [Issue#149]: 'stringified' indexes for enums
448+
// [databind#149]: 'stringified' indexes for enums
447449
public void testIndexAsString() throws Exception
448450
{
449451
// first, regular index ought to work fine
@@ -455,7 +457,7 @@ public void testIndexAsString() throws Exception
455457
assertSame(TestEnum.values()[1], en);
456458
}
457459

458-
// [Issue#745]
460+
// [databind#745]
459461
public void testDeserializerForCreatorWithEnumMaps() throws Exception
460462
{
461463
final ObjectMapper mapper = new ObjectMapper();
@@ -464,4 +466,19 @@ public void testDeserializerForCreatorWithEnumMaps() throws Exception
464466
new TypeReference<EnumMap<EnumWithCreator,String>>() {});
465467
assertEquals("value", value.get(EnumWithCreator.A));
466468
}
469+
470+
public void testEnumWithJsonPropertyRename() throws Exception
471+
{
472+
String json = MAPPER.writeValueAsString(new EnumWithPropertyAnno[] {
473+
EnumWithPropertyAnno.B, EnumWithPropertyAnno.A
474+
});
475+
assertEquals("[\"b\",\"a\"]", json);
476+
477+
// and while not really proper place, let's also verify deser while we're at it
478+
EnumWithPropertyAnno[] result = MAPPER.readValue(json, EnumWithPropertyAnno[].class);
479+
assertNotNull(result);
480+
assertEquals(2, result.length);
481+
assertSame(EnumWithPropertyAnno.B, result[0]);
482+
assertSame(EnumWithPropertyAnno.A, result[1]);
483+
}
467484
}

0 commit comments

Comments
 (0)