Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support @JsonProperty annotation on enum values #83

Merged
merged 3 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ protected void _findMethods() {

protected void _findMethods(final Class<?> currType)
{
if (currType == null || currType == Object.class) {
if (currType == null || currType == Object.class || currType == Enum.class) {
return;
}
// Start with base type methods (so overrides work)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package com.fasterxml.jackson.jr.annotationsupport;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.jr.ob.api.ReaderWriterModifier;
import com.fasterxml.jackson.jr.ob.api.ValueWriter;
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;
import com.fasterxml.jackson.jr.ob.impl.POJODefinition;

public class AnnotationBasedValueRWModifier
extends ReaderWriterModifier
{
public class AnnotationBasedValueRWModifier extends ReaderWriterModifier {
// Matches SER_ENUM code in ValueLocatorBase
protected static final int SER_ENUM = 23;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol. As a sidenote, it did change in 3.0 to 24 :)
(will change ValueLocatorBase to public to avoid this case)


/**
* Visibility settings to use for auto-detecting accessors.
*/
Expand All @@ -19,17 +27,48 @@ public AnnotationBasedValueRWModifier(JsonAutoDetect.Value visibility) {
}

@Override
public POJODefinition pojoDefinitionForDeserialization(JSONReader readContext,
Class<?> pojoType)
{
return AnnotationBasedIntrospector.pojoDefinitionForDeserialization(readContext,
pojoType, _visibility);
public POJODefinition pojoDefinitionForDeserialization(JSONReader readContext, Class<?> pojoType) {
return AnnotationBasedIntrospector.pojoDefinitionForDeserialization(readContext, pojoType, _visibility);
}

@Override
public POJODefinition pojoDefinitionForSerialization(JSONWriter writeContext, Class<?> pojoType) {
return AnnotationBasedIntrospector.pojoDefinitionForSerialization(writeContext, pojoType, _visibility);
}

@Override
public POJODefinition pojoDefinitionForSerialization(JSONWriter writeContext,
Class<?> pojoType) {
return AnnotationBasedIntrospector.pojoDefinitionForSerialization(writeContext,
pojoType, _visibility);
public ValueWriter overrideStandardValueWriter(JSONWriter writeContext, Class<?> type, int stdTypeId) {
if (stdTypeId == SER_ENUM) {
return new EnumWriter(type);
}
return null;
}

private static class EnumWriter implements ValueWriter {
private final Class<?> _valueType;
private final Map<String, String> enumMap;

public EnumWriter(Class<?> type) {
_valueType = type;
enumMap = new HashMap<String, String>();
Field[] fields = type.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(JsonProperty.class)) {
enumMap.put(field.getName(), field.getAnnotation(JsonProperty.class).value());
} else {
enumMap.put(field.getName(), field.getName());
}
}
}

@Override
public void writeValue(JSONWriter context, JsonGenerator g, Object value) throws IOException {
context.writeValue(enumMap.get(((Enum) value).name()));
}

@Override
public Class<?> valueType() {
return _valueType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.Arrays;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.jr.ob.JSON;

import junit.framework.TestCase;
Expand All @@ -10,6 +11,8 @@ public abstract class ASTestBase extends TestCase
{
protected enum ABC { A, B, C; }

protected enum ABCRename { @JsonProperty("A1") A, @JsonProperty("B1") B, C; }

protected static class NameBean {
protected String first, last;

Expand All @@ -25,7 +28,7 @@ public NameBean(String f, String l) {
public void setFirst(String n) { first = n; }
public void setLast(String n) { last = n; }
}

protected void verifyException(Throwable e, String... matches)
{
String msg = e.getMessage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,40 @@ public void testBasicRenameOnDeserialize() throws Exception
assertEquals("Bob", result._first);
assertEquals("Burger", result._last);
}

public void testEnumRenameOnSerialize() throws Exception
{
ABCRename inputA = ABCRename.A;
// default
assertEquals(a2q("\"A\""), JSON.std.asString(inputA));
// with annotations
assertEquals(a2q("\"A1\""), JSON_WITH_ANNO.asString(inputA));

ABCRename inputB = ABCRename.B;
// default
assertEquals(a2q("\"B\""), JSON.std.asString(inputB));
// with annotations
assertEquals(a2q("\"B1\""), JSON_WITH_ANNO.asString(inputB));

ABCRename inputC = ABCRename.C;
// default
assertEquals(a2q("\"C\""), JSON.std.asString(inputC));
// with annotations
assertEquals(a2q("\"C\""), JSON_WITH_ANNO.asString(inputC));
}

public void testEnumRenameOnDeserialize() throws Exception
{
String jsonA = a2q("\"A1\"");
ABCRename resultA = JSON_WITH_ANNO.beanFrom(ABCRename.class, jsonA);
assertEquals(ABCRename.A, resultA);

String jsonB = a2q("\"B1\"");
ABCRename resultB = JSON_WITH_ANNO.beanFrom(ABCRename.class, jsonB);
assertEquals(ABCRename.B, resultB);

String jsonC = a2q("\"C\"");
ABCRename resultC = JSON_WITH_ANNO.beanFrom(ABCRename.class, jsonC);
assertEquals(ABCRename.C, resultC);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class ValueReaderLocator
/* Caching
/**********************************************************************
*/

/**
* Set of {@link ValueReader}s that we have resolved
*/
Expand Down Expand Up @@ -100,7 +100,7 @@ public class ValueReaderLocator
/* Instance state, caching
/**********************************************************************
*/

/**
* Reusable lookup key; only used by per-thread instances.
*/
Expand Down Expand Up @@ -143,14 +143,14 @@ protected ValueReaderLocator(ValueReaderLocator base,
// create new cache as there may be custom writers:
_knownReaders = new ConcurrentHashMap<ClassKey, ValueReader>(10, 0.75f, 2);
_readerLock = new Object();

_features = base._features;
_readContext = base._readContext;
_readerProvider = rwp;
_readerModifier = rwm;
_typeResolver = base._typeResolver;
}

public final static ValueReaderLocator blueprint(ReaderWriterProvider rwp, ReaderWriterModifier rwm) {
return new ValueReaderLocator(rwp, rwm);
}
Expand All @@ -168,7 +168,7 @@ public ValueReaderLocator with(ReaderWriterModifier rwm) {
}
return new ValueReaderLocator(this, _readerProvider, rwm);
}

public ValueReaderLocator perOperationInstance(JSONReader r, int features) {
return new ValueReaderLocator(this, features & CACHE_FLAGS, r);
}
Expand All @@ -188,7 +188,7 @@ public ValueReaderLocator perOperationInstance(JSONReader r, int features) {
/* Public API, operations
/**********************************************************************
*/

/**
* Method used during deserialization to find handler for given
* non-generic type: will first check for already resolved (and cached) readers
Expand Down Expand Up @@ -295,10 +295,29 @@ protected ValueReader arrayReader(Class<?> contextType, Class<?> arrayType) {
}

protected ValueReader enumReader(Class<?> enumType) {
// Call pojoDefinitionForDeserialization so that the annotation support extension can get custom names for
// enum values
POJODefinition def = null;
if (_readerModifier != null) {
def = _readerModifier.pojoDefinitionForDeserialization(_readContext, enumType);
}
Map<String, Object> byName = new HashMap<String, Object>();
Object[] enums = enumType.getEnumConstants();
Map<String,Object> byName = new HashMap<String,Object>();
for (Object e : enums) {
byName.put(e.toString(), e);
if (def == null) {
for (Object e : enums) {
byName.put(e.toString(), e);
}
} else {
for (POJODefinition.Prop e : def.getProperties()) {
if (e.field != null && e.field.isEnumConstant()) {
try {
byName.put(e.name, e.field.get(null));
} catch (IllegalAccessException ex) {
// Don't believe that this should be possible, but raise it up just in case
throw new RuntimeException(ex);
}
}
}
}
return new EnumReader(enumType, enums, byName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ private int _modifyAndRegisterWriter(Class<?> rawType, ValueWriter w) {
}
return _registerWriter(rawType, w);
}

private int _registerWriter(Class<?> rawType, ValueWriter valueWriter) {
// Due to concurrent access, possible that someone might have added it
synchronized (_knownWriters) {
Expand All @@ -269,7 +269,7 @@ private int _registerWriter(Class<?> rawType, ValueWriter valueWriter) {
return typeId;
}
}

protected BeanPropertyWriter[] _resolveBeanForSer(Class<?> raw, POJODefinition beanDef)
{
final List<POJODefinition.Prop> rawProps = beanDef.getProperties();
Expand Down Expand Up @@ -310,6 +310,16 @@ protected BeanPropertyWriter[] _resolveBeanForSer(Class<?> raw, POJODefinition b
}
}
int typeId = _findSimpleType(type, true);
// Give plugin the opportunity to override standard value writer
if (_writerModifier != null && typeId != 0) {
Integer I = _knownSerTypes.get(new ClassKey(type, _features));
if (I == null) {
ValueWriter w = _writerModifier.overrideStandardValueWriter(_writeContext, type, typeId);
if (w != null) {
typeId = _registerWriter(type, w);
}
}
}
props.add(new BeanPropertyWriter(typeId, rawProp.name, rawProp.field, m));
}
int plen = props.size();
Expand Down