Alibaba FastJson支持对象中私有属性的json解析

时间:2023-02-16 11:57:47
项目中需要使用fastjson做数据处理,而且部分对象中的属性很多都是第三方的非public的,且没有getter/setter方法,找了很久没有找到fastjson对这个问题的解决。
所以自己动手,基于fastjson的源代码构造了单独的javabean的序列化器。
使用方式:直接使用封装好的MetaJsonUtil的工具方法即可。
fastjson版本为1.2.7
MetaJsonUtil.getJSONString(bean)
MetaJsonUtil 类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;

/**
* fastJson不支持对象中私有属性的转换
* Created by 魔力鸟 on 2015-10-15.
*/
public final class MetaJsonUtil {

/**
* 获取对象对应属性数据组成的JSON对象
* @param target
* @return
*/
public static JSONObject getJSONObject(Object target) {
return JSON.parseObject(getJSONString(target));
}

/**
* 获取对象对应属性数据组成的字符串
* @param target
* @return
*/
public static String getJSONString(Object target) {
if (target != null) {
SerializeConfig globalInstance = SerializeConfig.getGlobalInstance();
globalInstance.put(target.getClass(), new MetaBeanSerializer(target.getClass()));
return JSON.toJSONString(target, globalInstance);
}else{
return "{}";
}
}
}
用到的2个类:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.serializer.*;
import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.TypeUtils;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* fastjson不支持私有或保护属性的序列化,使用本类支持。
* 注意:仅实现了序列化,未实现反序列化
* Created by 张三丰 on 2015-10-14.
*/
public class MetaBeanSerializer extends JavaBeanSerializer {

private final FieldSerializer[] getters ;
private final FieldSerializer[] sortedGetters;
private int features = 0;
public MetaBeanSerializer(Class<?> clazz) {
this(clazz, null);
}
public MetaBeanSerializer(Class<?> clazz, Map<String, String> aliasMap) {
super(clazz, aliasMap);
this.features = TypeUtils.getSerializeFeatures(clazz);
{
List<FieldSerializer> getterList = new ArrayList<FieldSerializer>();
List<FieldInfo> fieldInfoList = MetaTypeUtils.computeGetters(clazz, aliasMap, false);

for (FieldInfo fieldInfo : fieldInfoList) {
getterList.add(createFieldSerializer(fieldInfo));
}

getters = getterList.toArray(new FieldSerializer[getterList.size()]);
}
{
List<FieldSerializer> getterList = new ArrayList<FieldSerializer>();
List<FieldInfo> fieldInfoList = MetaTypeUtils.computeGetters(clazz, aliasMap, true);

for (FieldInfo fieldInfo : fieldInfoList) {
getterList.add(createFieldSerializer(fieldInfo));
}

sortedGetters = getterList.toArray(new FieldSerializer[getterList.size()]);
}
}
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
throws IOException {
SerializeWriter out = serializer.getWriter();

if (object == null) {
out.writeNull();
return;
}

if (writeReference(serializer, object, features)) {
return;
}

final FieldSerializer[] getters;

if (out.isEnabled(SerializerFeature.SortField)) {
getters = this.sortedGetters;
} else {
getters = this.getters;
}

SerialContext parent = serializer.getContext();
serializer.setContext(parent, object, fieldName, this.features, features);

final boolean writeAsArray = isWriteAsArray(serializer);

try {
final char startSeperator = writeAsArray ? '[' : '{';
final char endSeperator = writeAsArray ? ']' : '}';
out.append(startSeperator);

if (getters.length > 0 && out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.incrementIndent();
serializer.println();
}

boolean commaFlag = false;

if (isWriteClassName(serializer, object, fieldType, fieldName)) {
Class<?> objClass = object.getClass();
if (objClass != fieldType) {
out.writeFieldName(JSON.DEFAULT_TYPE_KEY);
serializer.write(object.getClass());
commaFlag = true;
}
}

char seperator = commaFlag ? ',' : '\0';

char newSeperator = FilterUtils.writeBefore(serializer, object, seperator);
commaFlag = newSeperator == ',';

for (int i = 0; i < getters.length; ++i) {
FieldSerializer fieldSerializer = getters[i];

Field field = fieldSerializer.getField();
if (serializer.isEnabled(SerializerFeature.SkipTransientField)) {
if (field != null) {
if (Modifier.isTransient(field.getModifiers())) {
continue;
}
}
}

if (serializer.isEnabled(SerializerFeature.IgnoreNonFieldGetter)) {
if (field == null) {
continue;
}
}

if (!FilterUtils.applyName(serializer, object, fieldSerializer.getName())) {
continue;
}

if (!FilterUtils.applyLabel(serializer, fieldSerializer.getLabel())) {
continue;
}

Object propertyValue = fieldSerializer.getPropertyValue(object);

if (!FilterUtils.apply(serializer, object, fieldSerializer.getName(), propertyValue)) {
continue;
}

String key = FilterUtils.processKey(serializer, object, fieldSerializer.getName(), propertyValue);

Object originalValue = propertyValue;
propertyValue = FilterUtils.processValue(serializer, object, fieldSerializer.getName(), propertyValue);

if (propertyValue == null && !writeAsArray) {
if ((!fieldSerializer.isWriteNull())
&& (!serializer.isEnabled(SerializerFeature.WriteMapNullValue))) {
continue;
}
}

if (propertyValue != null && serializer.isEnabled(SerializerFeature.NotWriteDefaultValue)) {
Field field1 = ReflectionUtils.findField(FieldSerializer.class, "fieldInfo");
ReflectionUtils.makeAccessible(field1);
Class<?> fieldCLass = ((FieldInfo)ReflectionUtils.getField(field1,fieldSerializer)).getFieldClass();
if (fieldCLass == byte.class && propertyValue instanceof Byte
&& ((Byte) propertyValue).byteValue() == 0) {
continue;
} else if (fieldCLass == short.class && propertyValue instanceof Short
&& ((Short) propertyValue).shortValue() == 0) {
continue;
} else if (fieldCLass == int.class && propertyValue instanceof Integer
&& ((Integer) propertyValue).intValue() == 0) {
continue;
} else if (fieldCLass == long.class && propertyValue instanceof Long
&& ((Long) propertyValue).longValue() == 0L) {
continue;
} else if (fieldCLass == float.class && propertyValue instanceof Float
&& ((Float) propertyValue).floatValue() == 0F) {
continue;
} else if (fieldCLass == double.class && propertyValue instanceof Double
&& ((Double) propertyValue).doubleValue() == 0D) {
continue;
} else if (fieldCLass == boolean.class && propertyValue instanceof Boolean
&& !((Boolean) propertyValue).booleanValue()) {
continue;
}
}

if (commaFlag) {
out.append(',');
if (out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.println();
}
}

if (key != fieldSerializer.getName()) {
if (!writeAsArray) {
out.writeFieldName(key);
}
serializer.write(propertyValue);
} else if (originalValue != propertyValue) {
if (!writeAsArray) {
fieldSerializer.writePrefix(serializer);
}
serializer.write(propertyValue);
} else {
if (!writeAsArray) {
fieldSerializer.writeProperty(serializer, propertyValue);
} else {
fieldSerializer.writeValue(serializer, propertyValue);
}
}

commaFlag = true;
}

FilterUtils.writeAfter(serializer, object, commaFlag ? ',' : '\0');

if (getters.length > 0 && out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.decrementIdent();
serializer.println();
}

out.append(endSeperator);
} catch (Exception e) {
throw new JSONException("write javaBean error", e);
} finally {
serializer.setContext(parent);
}
}
}

public class MetaTypeUtils extends TypeUtils {

public static List<FieldInfo> computeGetters(Class<?> clazz, Map<String, String> aliasMap, boolean sorted) {
Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>();

for (Method method : clazz.getMethods()) {
String methodName = method.getName();
int ordinal = 0, serialzeFeatures = 0;
String label = null;

if (Modifier.isStatic(method.getModifiers())) {
continue;
}

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 = getSupperMethodAnnotation(clazz, method);
}

if (annotation != null) {
if (!annotation.serialize()) {
continue;
}

ordinal = annotation.ordinal();
serialzeFeatures = SerializerFeature.of(annotation.serialzeFeatures());

if (annotation.name().length() != 0) {
String propertyName = annotation.name();

if (aliasMap != null) {
propertyName = aliasMap.get(propertyName);
if (propertyName == null) {
continue;
}
}

fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, null, ordinal, serialzeFeatures, annotation.label()));
continue;
}

if (annotation.label().length() != 0) {
label = annotation.label();
}
}

if (methodName.startsWith("get")) {
if (methodName.length() < 4) {
continue;
}

if (methodName.equals("getClass")) {
continue;
}

char c3 = methodName.charAt(3);

String propertyName;
if (Character.isUpperCase(c3)) {
if (compatibleWithJavaBean) {
propertyName = decapitalize(methodName.substring(3));
} else {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}
} 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;
}

Field field = ParserConfig.getField(clazz, propertyName);

if (field != null) {
JSONField fieldAnnotation = field.getAnnotation(JSONField.class);

if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}

ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());

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;
}
}

fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, field, ordinal, serialzeFeatures, label));
}

if (methodName.startsWith("is")) {
if (methodName.length() < 3) {
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);
}
} else if (c2 == '_') {
propertyName = methodName.substring(3);
} else if (c2 == 'f') {
propertyName = methodName.substring(2);
} else {
continue;
}

Field field = ParserConfig.getField(clazz, propertyName);

if (field == null) {
field = ParserConfig.getField(clazz, methodName);
}

if (field != null) {
JSONField fieldAnnotation = field.getAnnotation(JSONField.class);

if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}

ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());

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;
}
}

fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, field, ordinal, serialzeFeatures, label));
}
}
//此处改动:获取所有属性
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}

JSONField fieldAnnotation = field.getAnnotation(JSONField.class);

int ordinal = 0, serialzeFeatures = 0;
String propertyName = field.getName();
String label = null;
if (fieldAnnotation != null) {
if (!fieldAnnotation.serialize()) {
continue;
}

ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());

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 (!fieldInfoMap.containsKey(propertyName)) {
fieldInfoMap.put(propertyName, new FieldInfo(propertyName, null, field, ordinal, serialzeFeatures, label));
}
}

List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>();

boolean containsAll = false;
String[] orders = null;

JSONType annotation = clazz.getAnnotation(JSONType.class);
if (annotation != null) {
orders = annotation.orders();

if (orders != null && orders.length == fieldInfoMap.size()) {
containsAll = true;
for (String item : orders) {
if (!fieldInfoMap.containsKey(item)) {
containsAll = false;
break;
}
}
} else {
containsAll = false;
}
}

if (containsAll) {
for (String item : orders) {
FieldInfo fieldInfo = fieldInfoMap.get(item);
fieldInfoList.add(fieldInfo);
}
} else {
for (FieldInfo fieldInfo : fieldInfoMap.values()) {
fieldInfoList.add(fieldInfo);
}

if (sorted) {
Collections.sort(fieldInfoList);
}
}
return fieldInfoList;
}
private static boolean isJSONTypeIgnore(Class<?> clazz, String propertyName) {
JSONType jsonType = clazz.getAnnotation(JSONType.class);

if (jsonType != null) {
// 1、新增 includes 支持,如果 JSONType 同时设置了includes 和 ignores 属性,则以includes为准。
// 2、个人认为对于大小写敏感的Java和JS而言,使用 equals() 比 equalsIgnoreCase() 更好,改动的唯一风险就是向后兼容性的问题
// 不过,相信开发者应该都是严格按照大小写敏感的方式进行属性设置的
String[] fields = jsonType.includes();
if (fields.length > 0) {
for (int i = 0; i < fields.length; i++) {
if (propertyName.equals(fields[i])) {
return false;
}
}
return true;
} else {
fields = jsonType.ignores();
for (int i = 0; i < fields.length; i++) {
if (propertyName.equals(fields[i])) {
return true;
}
}
}
}

if (clazz.getSuperclass() != Object.class && clazz.getSuperclass() != null) {
if (isJSONTypeIgnore(clazz.getSuperclass(), propertyName)) {
return true;
}
}

return false;
}
}
代码使用到了spring的反射工具类,所以依赖spring-core.

注意:理论上不存在问题,但未经过严格测试。