资讯专栏INFORMATION COLUMN

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

leiyi / 3144人阅读

摘要:而我现在直接返回的是对象。跟进去方法,在最后调用方法将值付给和再回到前面的重写方法最后返回转换好的字符串。到这里整个转换流程就已经结束了。

一、前言

最近老大说要新增一个试用广告的功能,我巴拉巴拉的从之前推送广告那里将代码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...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72922.html

相关文章

  • 高性能JSON框架FastJson的简单使用

    摘要:前言的介绍协议使用方便,越来越流行的处理器有很多这里我介绍一下是阿里的开源框架被不少企业使用是一个极其优秀的框架地址的特点数度快无论序列化和反序列化都是当之无愧的功能强大支持普通类包括任意或零依赖没有依赖其它任何类库的简单说明对于格式字符串 1.前言 1.1.FastJson的介绍: JSON协议使用方便,越来越流行,JSON的处理器有很多,这里我介绍一下FastJson,FastJs...

    Karrdy 评论0 收藏0
  • 把手你走进下一代的ES6模块打包工具—Rollup

    摘要:首先把这个示例仓库下载到本地准备就绪,正文开始简介以下内容基于和这两个打包工具来展开。但是目前,中的大多数包都是以模块的形式出现的。在它们更改之前,我们需要将模块转换为供处理。可以在中把注释掉看看打包后的文件,会把整个打包进来。 本文一共七个例子,由浅入深带你熟悉Rollup。首先把 rollup-demos 这个示例仓库下载到本地 mkdir rollup cd rollup git...

    李文鹏 评论0 收藏0
  • fastjson 的使用总结

    摘要:读取类路径下的配置文件解析成对象数组并返回读取类路径下的文件先将字符串转为数组将转化为实体类为属性赋值转成实体对象实体对象包含属性实体对象类型参考最佳实践介绍教程 showImg(https://segmentfault.com/img/bVbaqQi?w=400&h=300); 前言 最近在开发过程中使用了大量的json作为前后端数据交换的方式,由于之前没有对json做过系统的学习,...

    caohaoyu 评论0 收藏0
  • 轰轰烈烈的搭建Spring + Spring MVC + Mybatis

    摘要:配置和编码格式使用提供过滤器处理字符编码。请求从不改变资源的状态,无副作用。中添加,采用默认配置已经足够面对大多数场景注入参考链接手把手教你整合最优雅框架我的编码习惯接口定义设计最佳实践 创建项目 打开IDEA -> Create New Project勾选Create from archetypeshowImg(https://segmentfault.com/img/bV5tS4?...

    Fourierr 评论0 收藏0

发表评论

0条评论

leiyi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<