目的
1、枚舉值轉換成完全的json;
2、對象中的枚舉成員完全轉換成json;
3、枚舉類的全部值轉換成json;
枚舉定義

public enum SongsEnum {
SAFE_AND_SOUND(1,"Taylor Swift","Safe&Sound","2011-12-26")
,SHAKE_IT_OFF(2,"Taylor Swift","Shake It Off","2014-08-19")
,STYLE(3,"Taylor Swift","Style","2015-02-09")
,SOUND_OF_SILENCE(4,"Simon & Garfunkel","The Sound Of Silence","1966-01-17")
,BETTER_MAN(5,"Little Big Town","Better Man","2016-10-20")
,YESTERDAY_ONCE_MORE(6,"Carpenters","Yesterday Once More","1973-05-16")
;
public final int index;
public final String singer;
public final String name;
public final String date;
private SongsEnum(int seq, String singer, String name, String date) {
this.index = seq;
this.singer = singer;
this.name = name;
this.date = date;
}
public int getIndex() { return index; }
public String getSinger() { return singer; }
public String getName() { return name; }
public String getDate() { return date; }
}

一、枚舉值轉換json
期望結果:SongsEnum.SAFE_AND_SOUND -> {"date":"2011-12-26","index":1,"name":"Safe&Sound","singer":"Taylor Swift"}
## 默認調用結果
JSON.toJSONString(SongsEnum.SAFE_AND_SOUND) -> "SAFE_AND_SOUND" JSON.toJSONString(SongsEnum.BETTER_MAN,SerializerFeature.WriteEnumUsingName) -> "BETTER_MAN" JSON.toJSONString(SongsEnum.STYLE,SerializerFeature.WriteEnumUsingToString) -> "STYLE"
默認的使用fastjson轉換enum,那么得到的enum json可能不是想要的.
(1) 重寫enum的toString()
@Override
public String toString() {
return "{'name':"+this.name+",'singer':"+this.singer+"}";
}
JSON.toJSONString(SongsEnum.STYLE,SerializerFeature.WriteEnumUsingToString) -> "{'name':Style,'singer':Taylor Swift}"
雖然可以這樣得到想要的結果,但相對來說太麻煩,每個enum類都要從寫toString().
(此種方式並沒有研究,所以以上重寫toString()的代碼可能存在問題)
(2) fastjson的config設置
fastjson提供的JSON.toJSONString(...)有很多重載的方法,例如:
public static String toJSONString(Object object, SerializeConfig config, SerializerFeature... features)
所以,fastjson可以通過設置SerializeConfig來配置enum的序列化。

public static void main(String[] args) {
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(SongsEnum.class);
String s = JSON.toJSONString(SongsEnum.SOUND_OF_SILENCE, config);
System.out.println(s);
// {"date":"1966-01-17","index":4,"name":"The Sound Of Silence","singer":"Simon & Garfunkel"}
}

二、對象中的枚舉成員完全轉換成json

public enum StatusEnum {
STATUS_A(0,"狀態A"),
STATUS_B(1,"狀態B"),
STATUS_C(2,"狀態C");
public final int index;
public final String status;
StatusEnum(int i, String status) {
this.index = i;
this.status = status;
}
public int getIndex() { return index; }
public String getStatus() { return status; }
}


class JavaBean{
private String name;
private SongsEnum song;
private StatusEnum status;
public JavaBean(String name,SongsEnum song,StatusEnum status){
this.name = name;
this.song = song;
this.status = status;
}
//省略setter/getter
}

期望: {"name":"vegilyn","song":{"date":"2014-08-19","index":2,"name":"Shake It Off","singer":"Taylor Swift"},"status":{"index":1,"status":"狀態B"}}
默認結果:
JSON.toJSONString(new JavaBean("vegilyn",SongsEnum.SHAKE_IT_OFF,StatusEnum.STATUS_B)) -> {"name":"vegilyn","song":"SHAKE_IT_OFF","status":"STATUS_B"}
可以看出此結果和目的1中的結果一樣的,所以通過目的1的方式也可以解決。
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(SongsEnum.class); // 配置enum轉換
String s = JSON.toJSONString(new JavaBean("vegilyn",SongsEnum.SHAKE_IT_OFF,StatusEnum.STATUS_B),config);
System.out.println(s);
// {"name":"vegilyn","song":{"date":"2014-08-19","index":2,"name":"Shake It Off","singer":"Taylor Swift"},"status":"STATUS_B"}
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(SongsEnum.class,StatusEnum.class); // 配置enum轉換
String s = JSON.toJSONString(new JavaBean("vegilyn",SongsEnum.SHAKE_IT_OFF,StatusEnum.STATUS_B),config);
System.out.println(s);
// {"name":"vegilyn","song":{"date":"2014-08-19","index":2,"name":"Shake It Off","singer":"Taylor Swift"},"status":{"index":1,"status":"狀態B"}}
特別枚舉數組轉換的json:(注意重復值的json值)

public static void main(String[] args) {
SongsEnum[] songsEnums = {SongsEnum.BETTER_MAN, SongsEnum.SAFE_AND_SOUND, SongsEnum.BETTER_MAN};
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(SongsEnum.class);
System.out.println(JSON.toJSONString(songsEnums,config));
}
// [{"date":"2016-10-20","index":5,"name":"Better Man","singer":"Little Big Town"},{"date":"2011-12-26","index":1,"name":"Safe&Sound","singer":"Taylor Swift"},{"$ref":"$[0]"}]

三、枚舉類的全部值轉換成json
期望結果:

[
{"index": 1, "singer": "Taylor Swift", "name": "Safe&Sound", "date": "2011-12-26"},
{"index": 2, "singer": "Taylor Swift", "name": "Shake It Off", "date": "2014-08-19"},
{"index": 3, "singer": "Taylor Swift", "name": "Style", "date": "2015-02-09"},
{"index": 4, "singer": "Simon & Garfunkel", "name": "The Sound Of Silence", "date": "1966-01-17"},
{"index": 5, "singer": "Little Big Town", "name": "Better Man", "date": "2016-10-20"},
{"index": 6, "singer": "Carpenters", "name": "Yesterday Once More", "date": "1973-05-16"}
]

實現代碼:

public static void main(String[] args) {
SongsEnum[] values = SongsEnum.values();
List<SongsEnum> songsEnums = new ArrayList<SongsEnum>();
for (SongsEnum value : values) {
songsEnums.add(value);
}
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(SongsEnum.class);
System.out.println(JSON.toJSONString(songsEnums, config));
}

以上能達到想要的效果,但是,每個enum類都要重復寫以上代碼。所以,利用反射來寫一個公共的方法。

public static String toJson(Class<? extends Enum> enumClass) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method methodValues = enumClass.getMethod("values");
Object invoke = methodValues.invoke(null);
int length = java.lang.reflect.Array.getLength(invoke);
List<Object> values = new ArrayList<Object>();
for (int i=0; i<length; i++) {
values.add(java.lang.reflect.Array.get(invoke, i));
}
SerializeConfig config = new SerializeConfig();
config.configEnumAsJavaBean(enumClass);
return JSON.toJSONString(values,config);
}


public static void main(String[] args) {
try {
System.out.println(EnumJsonUtil.toJson(StatusEnum.class));
// [{"index":0,"status":"狀態A"},{"index":1,"status":"狀態B"},{"index":2,"status":"狀態C"}]
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

四、擴展:fastjson如何得到enum的field定義。
通過jdk反射機制得到的field。

public static void main(String[] args) {
Field[] fields = SongsEnum.class.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
}
SAFE_AND_SOUND
SHAKE_IT_OFF
STYLE
SOUND_OF_SILENCE
BETTER_MAN
YESTERDAY_ONCE_MORE
index
singer
name
date

如上,如果是通過jdk反射機制得到class的filed。會發現得到了不想要的結果,ex: SAFE_AND_SOUND 、SHAKE_IT_OFF 等。
想得到的field其實只想要: index、singer、name、date。
當然也可以自己分析,然后過濾出自己想要的class的field。
但下面看下fastjson中怎么得到enum中期望的field。(fastjson版本:1.2.31)
(1) 跟蹤代碼: new SerializeConfig().configEnumAsJavaBean(enumClass)
public void configEnumAsJavaBean(Class<? extends Enum>... enumClasses) {
for (Class<? extends Enum> enumClass : enumClasses) {
put(enumClass, createJavaBeanSerializer(enumClass));
}
}

private final ObjectSerializer createJavaBeanSerializer(Class<?> clazz) {
SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy, fieldBase);
if (beanInfo.fields.length == 0 && Iterable.class.isAssignableFrom(clazz)) {
return MiscCodec.instance;
}
return createJavaBeanSerializer(beanInfo);
}


public class SerializeBeanInfo {
protected final Class<?> beanType;
protected final String typeName;
protected final JSONType jsonType;
protected final FieldInfo[] fields;
protected final FieldInfo[] sortedFields;
protected int features;
public SerializeBeanInfo(Class<?> beanType, //
JSONType jsonType, //
String typeName, //
int features,
FieldInfo[] fields, //
FieldInfo[] sortedFields
){
this.beanType = beanType;
this.jsonType = jsonType;
this.typeName = typeName;
this.features = features;
this.fields = fields;
this.sortedFields = sortedFields;
}
}

可以看到SerializeBeanInfo 中定義的有 fields 。
(很好奇為什么定義成 protected , TyoeUtils.buildBeanInfo(...)是public的,返回的SerializeBeanInfo 也是public。
但是,SerializeBeanInfo. fields卻是protected 的。
導致我知道fastjson中有這么一個util方法,可以得到我想要的enum信息(更確切的是可以得到class的信息)。
但是,最后我並不能直接在我的代碼中使用SerializeBeanInfo。)。
通過debug可以看到SerializeBeanInfo對象的屬性,就是我想要的結果。
接着進去看TyoeUtils.buildBeanInfo(...)的實現:

public static SerializeBeanInfo buildBeanInfo(Class<?> beanType //
, Map<String, String> aliasMap //
, PropertyNamingStrategy propertyNamingStrategy //
, boolean fieldBased //
) {
JSONType jsonType = beanType.getAnnotation(JSONType.class);
// fieldName,field ,先生成fieldName的快照,減少之后的findField的輪詢
Map<String, Field> fieldCacheMap = new HashMap<String, Field>();
ParserConfig.parserAllFieldToCache(beanType, fieldCacheMap);
List<FieldInfo> fieldInfoList = fieldBased
? computeGettersWithFieldBase(beanType, aliasMap, false, propertyNamingStrategy) //
: computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, false, propertyNamingStrategy);
// 省略...
}

執行computeGetters(...)的結果:

public static List<FieldInfo> computeGetters(Class<?> clazz, //
JSONType jsonType, //
Map<String, String> aliasMap, //
Map<String, Field> fieldCacheMap, //
boolean sorted, //
PropertyNamingStrategy propertyNamingStrategy //
) {
Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>();
for (Method method : clazz.getMethods()) {
String methodName = method.getName();
int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
String label = null;
if (Modifier.isStatic(method.getModifiers())) { continue; } // 跳過static method
if (method.getReturnType().equals(Void.TYPE)) { continue; }
if (method.getParameterTypes().length != 0) { continue; }
if (method.getReturnType() == ClassLoader.class) { continue; }
if (method.getName().equals("getMetaClass")
&& method.getReturnType().getName().equals("groovy.lang.MetaClass")) {
continue;
}
JSONField annotation = method.getAnnotation(JSONField.class);
if (annotation == null) {
annotation = getSuperMethodAnnotation(clazz, method);
}
if (annotation != null) {
if (!annotation.serialize()) { continue; }
ordinal = annotation.ordinal();
serialzeFeatures = SerializerFeature.of(annotation.serialzeFeatures());
parserFeatures = Feature.of(annotation.parseFeatures());
if (annotation.name().length() != 0) {
String propertyName = annotation.name();
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
FieldInfo fieldInfo = new FieldInfo(propertyName, method, null, clazz, null, ordinal,
serialzeFeatures, parserFeatures, annotation, null, label);
fieldInfoMap.put(propertyName, fieldInfo);
continue;
}
if (annotation.label().length() != 0) {
label = annotation.label();
}
}
if (methodName.startsWith("get")) {
if (methodName.length() < 4) { continue; }
if (methodName.equals("getClass")) { continue;}
if (methodName.equals("getDeclaringClass") && clazz.isEnum()) { continue; }
char c3 = methodName.charAt(3);
String propertyName;
if (Character.isUpperCase(c3) //
|| c3 > 512 // for unicode method name
) {
if (compatibleWithJavaBean) {
propertyName = decapitalize(methodName.substring(3));
} else {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}
propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName,3);
} else if (c3 == '_') {
propertyName = methodName.substring(4);
} else if (c3 == 'f') {
propertyName = methodName.substring(3);
} else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {
propertyName = decapitalize(methodName.substring(3));
} else {
continue;
}
boolean ignore = isJSONTypeIgnore(clazz, propertyName);
if (ignore) {
continue;
}
//假如bean的field很多的情況一下,輪詢時將大大降低效率
Field field = ParserConfig.getFieldFromCache(propertyName, fieldCacheMap);
if (field == null && propertyName.length() > 1) {
char ch = propertyName.charAt(1);
if (ch >= 'A' && ch <= 'Z') {
String javaBeanCompatiblePropertyName = decapitalize(methodName.substring(3));
field = ParserConfig.getFieldFromCache(javaBeanCompatiblePropertyName, fieldCacheMap);
}
}
JSONField fieldAnnotation = null;
if (field != null) {
fieldAnnotation = field.getAnnotation(JSONField.class);
if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}
ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
if (fieldAnnotation.name().length() != 0) {
propertyName = fieldAnnotation.name();
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
}
if (fieldAnnotation.label().length() != 0) {
label = fieldAnnotation.label();
}
}
}
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}
FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
annotation, fieldAnnotation, label);
fieldInfoMap.put(propertyName, fieldInfo);
}
if (methodName.startsWith("is")) {
if (methodName.length() < 3) {
continue;
}
if (method.getReturnType() != Boolean.TYPE
&& method.getReturnType() != Boolean.class) {
continue;
}
char c2 = methodName.charAt(2);
String propertyName;
if (Character.isUpperCase(c2)) {
if (compatibleWithJavaBean) {
propertyName = decapitalize(methodName.substring(2));
} else {
propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
}
propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName,2);
} else if (c2 == '_') {
propertyName = methodName.substring(3);
} else if (c2 == 'f') {
propertyName = methodName.substring(2);
} else {
continue;
}
Field field = ParserConfig.getFieldFromCache(propertyName,fieldCacheMap);
if (field == null) {
field = ParserConfig.getFieldFromCache(methodName,fieldCacheMap);
}
JSONField fieldAnnotation = null;
if (field != null) {
fieldAnnotation = field.getAnnotation(JSONField.class);
if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}
ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
if (fieldAnnotation.name().length() != 0) {
propertyName = fieldAnnotation.name();
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
}
if (fieldAnnotation.label().length() != 0) {
label = fieldAnnotation.label();
}
}
}
if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}
//優先選擇get
if (fieldInfoMap.containsKey(propertyName)) {
continue;
}
FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
annotation, fieldAnnotation, label);
fieldInfoMap.put(propertyName, fieldInfo);
}
} // for methods end
// for methods : 優先通過定義的method來獲取FieldInfo。
// 然后,才通過Fields得到沒有定義類似getXXX的field。
Field[] fields = clazz.getFields();
computeFields(clazz, aliasMap, propertyNamingStrategy, fieldInfoMap, fields);
return getFieldInfos(clazz, sorted, fieldInfoMap);
}

如果枚舉中的: index、singer、name、date。沒有定義getter方法,那么fieldInfoMap在for methods end之后是empty的。
並且經過測試發現, 如果class中定義了: public final static String testName = "test";
只有提供了 : public String getTestName(){return testName;} 才會被fastjson轉換。注:不能是public static方法。(原因看上面的源碼)
或者 定義成 public final String testName = "test"; (但明顯沒有static的含義所在,原因看下面源碼)

private static void computeFields(
Class<?> clazz, //
Map<String, String> aliasMap, //
PropertyNamingStrategy propertyNamingStrategy, //
Map<String, FieldInfo> fieldInfoMap, //
Field[] fields) {
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) { // 如果是static 修飾的field 跳過。
continue;
}
JSONField fieldAnnotation = field.getAnnotation(JSONField.class); // fastjson提供的注解
int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
String propertyName = field.getName();
String label = null;
if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}
ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
if (fieldAnnotation.name().length() != 0) {
propertyName = fieldAnnotation.name();
}
if (fieldAnnotation.label().length() != 0) {
label = fieldAnnotation.label();
}
}
if (aliasMap != null) { // 別名定義
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}
if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}
if (!fieldInfoMap.containsKey(propertyName)) {// map中不存在該field的FieldInfo, 則創建一個
FieldInfo fieldInfo = new FieldInfo(propertyName, null, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
null, fieldAnnotation, label);
fieldInfoMap.put(propertyName, fieldInfo);
}
}
}

所以,根據枚舉的特性。ex: SAFE_AND_SOUND 、SHAKE_IT_OFF 等。在enum中的定義都是 public static final ...
並且在methods中也不存在getXXX。
如果,我在enum中定義一個method: getSAFE_AND_SOUND(),則field中會出現。




