Skip to content

Commit

Permalink
Merge pull request #83 from MikeDombo/jsonproperty-enum
Browse files Browse the repository at this point in the history
feature: support JsonProperty annotation on enum values
  • Loading branch information
cowtowncoder authored May 14, 2021
2 parents 24c36c4 + 7805a68 commit b0a5f91
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,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 Expand Up @@ -415,7 +415,7 @@ protected void _checkSetterMethod(Method m)
protected boolean _isFieldVisible(Field f) {
// Consider transient and static-final to be non-visible
// TODO: (maybe?) final
return !(Modifier.isFinal(f.getModifiers()) && Modifier.isStatic(f.getModifiers()))
return !(Modifier.isFinal(f.getModifiers()) && Modifier.isStatic(f.getModifiers()) && !f.isEnumConstant())
&& !Modifier.isTransient(f.getModifiers())
&& _visibility.getFieldVisibility().isVisible(f);
}
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;

/**
* 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

0 comments on commit b0a5f91

Please sign in to comment.