于一次JSON格式错误 之 手把手带你走一波FastJSON将对象转成JSON字符串流程

2次阅读

共计 8039 个字符,预计需要花费 21 分钟才能阅读完成。

一、前言
最近老大说要新增一个试用广告的功能,我巴拉巴拉的从之前推送广告那里将代码 cv 过来,然后跟老大说搞定了!过一会老大说返回的 json 格式不对!于是乎我瞧了瞧:
{
“adsArea1”:{
“adsMaterials”:[
{
“animType”:”99″,
“materialName”:”b6d767d2f8ed5d21a44b0e5886680cb9.jpg”,
“materialType”:”0″,
“materialUrl”:”http://xxx/059492d7-66f1-4052-891a-c512f957342d22.jpg”,
“playTime”:5,
“sort”:1
}],
“areaDirection”:””,
“areaId”:”3721″,
“areaType”:”0″,
“createBy”:””,// 空值
“createDate”:””,// 空值
“deleteFlg”:””,// 空值
“height”:800,
“id”:”418″,
“materialList”:””,// 空值
“playSettingList”:””,// 空值
“updateBy”:””,
“updateDate”:””,// 空值
“width”:1280,
“x”:0,
“y”:0},

···
}
咋看一下,感觉没什么问题啊?然后他说之前返给我的格式不是这样的,没有这些中间的这些空值,那好吧,我看了下以往的推送历史记录里,确实是没有的,那返回的是一样的对象怎么就没有了呢?仔细看了下,在推送的时候,保存在推送历史表里的记录是转成 json 字符串存储起来的 String json = JSON.toJSONString(pushAdvertisementDto); 而存在表里的数据则没有 null 值。而我现在直接返回的是对象。那么问题就出在这里了~
二、直击源码
点进去 JSON.class 里面进去看 toJSONString(pushAdvertisementDto)方法 发现可以带上一些参数:

让我们来看看 SerializerFeature 是一个什么东西吧:

在网上搜索了一下名词解释:

就是这个东西默认是 false 的,默认不输出 null 值的字段,如果想携带该字段,那么用这个方法就可以了:JSON.toJSONString(Object object, SerializerFeature.WriteMapNullValue)
问题到这里就已经知道是为什么了,返回的数据没有经过 FastJSON 转换,因为用的是 @RestController,我默认返回的对象传回去安卓那里也是 json 格式,但是里面的字段不对,因为业务原因就只定义了一个 PushAdvertisementDto 对象返回,所以在这里用 FastJSON 转一下再返回就可以了。
我们接着往下看:JSON.toJSONString() 本质还是调的这个方法:
public static String toJSONString(Object object, int defaultFeatures, SerializerFeature… features) {
SerializeWriter out = new SerializeWriter((Writer)null, defaultFeatures, features); // 1

String var5;
try {
JSONSerializer serializer = new JSONSerializer(out);
serializer.write(object);// 2
var5 = out.toString();
} finally {
out.close();
}

return var5;
}
1. 如果我们要输出为 null 值的字段,则 SerializerFeature.WriteMapNullValue 会被传到 SerializerWriter 这里做了一个初始化:
public SerializeWriter(Writer writer, int defaultFeatures, SerializerFeature… features) {
this.writer = writer;
this.buf = (char[])bufLocal.get();
if (this.buf != null) {
bufLocal.set((Object)null);
} else {
this.buf = new char[2048];
}

int featuresValue = defaultFeatures;
SerializerFeature[] var5 = features;
int var6 = features.length;

for(int var7 = 0; var7 < var6; ++var7) {
SerializerFeature feature = var5[var7];
featuresValue |= feature.getMask();
}

this.features = featuresValue;
this.computeFeatures();
}
2. 让我们跟进来 serializer.write(object); 里面看一看:
public final void write(Object object) {
if (object == null) {
this.out.writeNull();
} else {
Class<?> clazz = object.getClass();
ObjectSerializer writer = this.getObjectWriter(clazz);

try {
writer.write(this, object, (Object)null, (Type)null, 0);// 在这里做了写的操作
} catch (IOException var5) {
throw new JSONException(var5.getMessage(), var5);
}
}
}
跟进去 writer.write(this, object, (Object)null, (Type)null, 0);
public interface ObjectSerializer {
void write(JSONSerializer var1, Object var2, Object var3, Type var4, int var5) throws IOException;
}
发现是个可拓展的接口,让我们看看 ObjectSerializer 具体的实现类 JavaBeanSerializer,这里代码比较长,虽然重点关注的代码比较少,但是还是完整的贴出来:
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
SerializeWriter out = serializer.out;
if (object == null) {
out.writeNull();
} else if (!this.writeReference(serializer, object, features)) {
FieldSerializer[] getters;
if (out.sortField) {
getters = this.sortedGetters;
} else {
getters = this.getters;
}

SerialContext parent = serializer.context;
serializer.setContext(parent, object, fieldName, this.beanInfo.features, features);
boolean writeAsArray = this.isWriteAsArray(serializer, features);

try {
char startSeperator = writeAsArray ? 91 : 123;
char endSeperator = writeAsArray ? 93 : 125;

out.append((char)startSeperator);// 这里开始拼接输出

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

boolean commaFlag = false;
if ((this.beanInfo.features & SerializerFeature.WriteClassName.mask) != 0 || serializer.isWriteClassName(fieldType, object)) {
Class<?> objClass = object.getClass();
if (objClass != fieldType) {
this.writeClassName(serializer, object);
commaFlag = true;
}
}

char seperator = commaFlag ? 44 : 0;
boolean directWritePrefix = out.quoteFieldNames && !out.useSingleQuotes;
char newSeperator = this.writeBefore(serializer, object, (char)seperator);
commaFlag = newSeperator == ‘,’;
boolean skipTransient = out.isEnabled(SerializerFeature.SkipTransientField);
boolean ignoreNonFieldGetter = out.isEnabled(SerializerFeature.IgnoreNonFieldGetter);

for(int i = 0; i < getters.length; ++i) {
FieldSerializer fieldSerializer = getters[i];
Field field = fieldSerializer.fieldInfo.field;
FieldInfo fieldInfo = fieldSerializer.fieldInfo;
String fieldInfoName = fieldInfo.name;
Class<?> fieldClass = fieldInfo.fieldClass;
if ((!skipTransient || field == null || !fieldInfo.fieldTransient) && (!ignoreNonFieldGetter || field != null) && this.applyName(serializer, object, fieldInfo.name) && this.applyLabel(serializer, fieldInfo.label)) {
Object propertyValue;
try {
propertyValue = fieldSerializer.getPropertyValue(object);
} catch (InvocationTargetException var32) {
if (!out.isEnabled(SerializerFeature.IgnoreErrorGetter)) {
throw var32;
}

propertyValue = null;
}

if (this.apply(serializer, object, fieldInfoName, propertyValue)) {
String key = this.processKey(serializer, object, fieldInfoName, propertyValue);
Object originalValue = propertyValue;
propertyValue = this.processValue(serializer, fieldSerializer.fieldContext, object, fieldInfoName, propertyValue);
if (propertyValue != null || writeAsArray || fieldSerializer.writeNull || out.isEnabled(SerializerFeature.WriteMapNullValue)) {
if (propertyValue != null && out.notWriteDefaultValue) {
Class<?> fieldCLass = fieldInfo.fieldClass;
if (fieldCLass == Byte.TYPE && propertyValue instanceof Byte && (Byte)propertyValue == 0 || fieldCLass == Short.TYPE && propertyValue instanceof Short && (Short)propertyValue == 0 || fieldCLass == Integer.TYPE && propertyValue instanceof Integer && (Integer)propertyValue == 0 || fieldCLass == Long.TYPE && propertyValue instanceof Long && (Long)propertyValue == 0L || fieldCLass == Float.TYPE && propertyValue instanceof Float && (Float)propertyValue == 0.0F || fieldCLass == Double.TYPE && propertyValue instanceof Double && (Double)propertyValue == 0.0D || fieldCLass == Boolean.TYPE && propertyValue instanceof Boolean && !(Boolean)propertyValue) {
continue;
}
}

if (commaFlag) {
out.write(44);
if (out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.println();
}
}

if (key != fieldInfoName) {
if (!writeAsArray) {
out.writeFieldName(key, true);
}

serializer.write(propertyValue);
} else if (originalValue != propertyValue) {
if (!writeAsArray) {
fieldSerializer.writePrefix(serializer);
}

serializer.write(propertyValue);
} else {
if (!writeAsArray) {
if (directWritePrefix) {
out.write(fieldInfo.name_chars, 0, fieldInfo.name_chars.length);
} else {
fieldSerializer.writePrefix(serializer);
}
}

if (!writeAsArray) {
if (fieldClass == String.class) {
if (propertyValue == null) {
if ((out.features & SerializerFeature.WriteNullStringAsEmpty.mask) == 0 && (fieldSerializer.features & SerializerFeature.WriteNullStringAsEmpty.mask) == 0) {
out.writeNull();
} else {
out.writeString(“”);
}
} else {
String propertyValueString = (String)propertyValue;
if (out.useSingleQuotes) {
out.writeStringWithSingleQuote(propertyValueString);
} else {
out.writeStringWithDoubleQuote(propertyValueString, ‘\u0000’);
}
}
} else {
fieldSerializer.writeValue(serializer, propertyValue);
}
} else {
fieldSerializer.writeValue(serializer, propertyValue);
}
}

commaFlag = true;
}
}
}
}

this.writeAfter(serializer, object, (char)(commaFlag ? ‘,’ : ‘\u0000’));
if (getters.length > 0 && out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.decrementIdent();
serializer.println();
}

out.append((char)endSeperator); // 结束拼接

} catch (Exception var33) {
String errorMessage = “write javaBean error”;
if (object != null) {
errorMessage = errorMessage + “, class ” + object.getClass().getName();
}

if (fieldName != null) {
errorMessage = errorMessage + “, fieldName : ” + fieldName;
}

if (var33.getMessage() != null) {
errorMessage = errorMessage + “, ” + var33.getMessage();
}

throw new JSONException(errorMessage, var33);
} finally {
serializer.context = parent;
}
}
}
我们挑重点的看,这里我们关注 out.append() 代码就可以了,中间大致就是判断一些前文说的 SerializerFeature.WriteMapNullValue 等条件。
跟进去 out.append() 方法,在最后调用 wirte 方法将值付给 this.buf 和 this.count:
public void write(int c) {
int newcount = this.count + 1;
if (newcount > this.buf.length) {
if (this.writer == null) {
this.expandCapacity(newcount);
} else {
this.flush();
newcount = 1;
}
}

this.buf[this.count] = (char)c;
this.count = newcount;
}
再回到前面的 var5 = out.toString();SerializeWriter 重写 toString 方法最后返回转换好的 json 字符串。到这里整个 json 转换流程就已经结束了。
三、结语
虽然只是一开始只是 json 格式的一个小问题,花了挺长的时间观看源码,但是感觉收获还是很大的,也很值。因为个人水平原因也不是全能看懂。。中间很长的那段源码大致就是用反射获取要转成 json 的对象的属性和值,然后中间经过一些条件判断,通过输出流拼接字符输出字符串。
如文中有不对的地方,欢迎指出一起讨论 ~
关于 SerializerFeature 名词解释源自:https://blog.csdn.net/u010246…

正文完
 0