fastjson-对象转json字符串时使用@JSONType注解指定对象字段的顺序

一、简单例子

依赖:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>

源码:

package com.junjie.test;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

public class Test {
    public static void main(String[] args) {
        User user = User.builder().userName("01junjie").age("12").atext("程序员").build();

//        JSONObject jsonObject = new JSONObject(true);
//        jsonObject.put("User",user);
//        String s = jsonObject.toJSONString();

        String s = JSONObject.toJSONString(user);
        System.out.println(s);

    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
//    @JSONType(orders = {"userName","age","atext"},ignores = {"age"})
    static class User{
        String userName;
        String age;
        String atext;
    }
}

输出结果:

{“age”:“12”,“atext”:“程序员”,“userName”:“01junjie”}

从结果上看,我们的程序正常输出,但是字段却出现了乱序,某些时候,我们需要指定顺序,我们可以将上述注释的注解@JSONType放开。

 package com.junjie.test;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

public class Test {
    public static void main(String[] args) {
        User user = User.builder().userName("01junjie").age("12").atext("程序员").build();

//        JSONObject jsonObject = new JSONObject(true);
//        jsonObject.put("User",user);
//        String s = jsonObject.toJSONString();

        String s = JSONObject.toJSONString(user);
        System.out.println(s);

    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @JSONType(orders = {"userName","age","atext"},ignores = {"age"})
    static class User{
        String userName;
        String age;
        String atext;
    }
}

输出结果:

{“userName”:“01junjie”,“atext”:“程序员”}

 @JSONType(orders = {"userName","age","atext"},ignores = {"age"})

这行代码的作用是,指定了字段的固定顺序,以及要排除的字段。

二、@JSONType简单介绍

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface JSONType {

    boolean asm() default true;

    String[] orders() default {};

    /**
     * @since 1.2.6
     */
    String[] includes() default {};

    String[] ignores() default {};

	、、、、
	、、、、
}

这是该注解的部分源码,这几个属性已经基本够用了,简单介绍一下作用:
orders:生成json时,指定字段按顺序排序
includes:生成json时,指定包含的字段
ignores:生成json时,指定忽略的字段

有时若觉得使用这个注解比较繁琐,也可以配合PropertyPreFilters 一起使用,可参考:
https://blog.csdn.net/weixin_41979002/article/details/122100260

三、@JSONType源码解析

若上述已经解决你的问题了,赶时间的,就可以不用往下看了,下面进入正题。

3.1 看源码的原因

由于业务需求,需要对java对象转json后,指定字段的固定顺序。

提前说明一下,本人在今天之前,是不曾知道fastjson这个依赖有这个注解的,更不知道该注解的存在及作用,所以起初一直百度都没有找到有@JSONType相关的解决方案(显然,就是我的描述问题,没描述到点子上),这也就映射出一个问题,许多工具也许帮你实现了很多功能,但你不知道也不懂使用,以至于百度也由于你的问题描述偏差,导致总与答案失之交臂。

这个时候该这么办,答案就是看源码。

3.2 分析源码的流程

3.2.1 本人的代码

    public static void main(String[] args) {
        User user = User.builder().userName("01junjie").age("12").atext("程序员").build();
//        JSONObject jsonObject = new JSONObject();
//        jsonObject.put("User",user);
//        String s = jsonObject.toJSONString();
        String s = JSONObject.toJSONString(user);
        System.out.println(s);

    }

3.2.2 toJSONString就是我们的入口

我们追溯到底后可以看到,最终都会调用下面这个接口的方法:

public interface ObjectSerializer {
    
    void write(JSONSerializer serializer, //
               Object object, //
               Object fieldName, //
               Type fieldType, //
               int features) throws IOException;
}

在这里插入图片描述

而这个接口有许多实现类,因此我们无法知道我们的程序运行了哪个实现类的实现方法,于是我们回退上一步调用该方法的的入口:

    public final void write(Object object) {
        if (object == null) {
            out.writeNull();
            return;
        }

        Class<?> clazz = object.getClass();
        ObjectSerializer writer = getObjectWriter(clazz);

        try {
            writer.write(this, object, null, null, 0);
        } catch (IOException e) {
            throw new JSONException(e.getMessage(), e);
        }
    }

ObjectSerializer writer = getObjectWriter(clazz);这行代码,便是获取一个Serializer,我们可以打上断点,看一下获取到的是哪个实现类,如下:
在这里插入图片描述
此种分析源码的方式很常用,从这里可以看到,该行代码可以根据我们的java对象创建出一个专属的Serializer。(如何创建的,不需要追究,不重要)

到了这里,我们已经能确定了,将我们的user对象转换为json字符串的主要实现类是JavaBeanSerializer

3.2.3 进入JavaBeanSerializer

在分析JavaBeanSerializer之前,我们先要明确自己的需求和目标:
1、为啥user的字段会出现乱序?
我们要找到方法中将这些字段转被换成字符串的代码位置,再一层层往上找。

先看该类实现的write:

protected void write(JSONSerializer serializer, //
                      Object object, //
                      Object fieldName, //
                      Type fieldType, //
                      int features,
                      boolean unwrapped
    ) throws IOException {
       //省略部分代码

        final FieldSerializer[] getters;

        if (out.sortField) {
            getters = this.sortedGetters;
        } else {
            getters = this.getters;
        }

        SerialContext parent = serializer.context;
        if (!this.beanInfo.beanType.isEnum()) {
            serializer.setContext(parent, object, fieldName, this.beanInfo.features, features);
        }

        final boolean writeAsArray = isWriteAsArray(serializer, features);

        FieldSerializer errorFieldSerializer = null;
        try {
            final char startSeperator = writeAsArray ? '[' : '{';
            final char endSeperator = writeAsArray ? ']' : '}';
            if (!unwrapped) {
                out.append(startSeperator);
            }
        //省略部分代码

getters 便是我们的目标,打上断点,再运行
在这里插入图片描述
可以看到sortedGetters里面的直就已经出现了乱序(重新做了排序),排在第一位的就是age。
在这里插入图片描述
我们想要的是
static class User{
String userName;
String age;
String atext;
}
userName、age、atext这样的顺序才对。

于是我们可以搜索一下sortedGetters是在哪个位置做的初始化,如下:

    public JavaBeanSerializer(SerializeBeanInfo beanInfo) {
        this.beanInfo = beanInfo;
        
        sortedGetters = new FieldSerializer[beanInfo.sortedFields.length];
        for (int i = 0; i < sortedGetters.length; ++i) {
            sortedGetters[i] = new FieldSerializer(beanInfo.beanType, beanInfo.sortedFields[i]);
        }
        //省略部分代码

sortedGetters 来自于beanInfo
在这里插入图片描述
进入SerializeBeanInfo:

public class SerializeBeanInfo {

    protected final Class<?> beanType;
    protected final String   typeName;
    protected final String   typeKey;
    protected final JSONType jsonType;

    protected final FieldInfo[] fields;
    protected final FieldInfo[] sortedFields;
    
    protected int               features;

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

}

再看看哪里调用了这个类的构造方法,如下:

public static SerializeBeanInfo buildBeanInfo(Class<?> beanType //
            , Map<String,String> aliasMap //
            , PropertyNamingStrategy propertyNamingStrategy //
            , boolean fieldBased //
    ){
        JSONType jsonType = TypeUtils.getAnnotation(beanType,JSONType.class);
        String[] orders = null;
        final int features;
        String typeName = null, typeKey = null;
        if(jsonType != null){
            orders = jsonType.orders();

            typeName = jsonType.typeName();
            if(typeName.length() == 0){
                typeName = null;
            }

            PropertyNamingStrategy jsonTypeNaming = jsonType.naming();
            if (jsonTypeNaming != PropertyNamingStrategy.CamelCase) {
                propertyNamingStrategy = jsonTypeNaming;
            }

            features = SerializerFeature.of(jsonType.serialzeFeatures());
            for(Class<?> supperClass = beanType.getSuperclass()
                ; supperClass != null && supperClass != Object.class
                    ; supperClass = supperClass.getSuperclass()){
                JSONType superJsonType = TypeUtils.getAnnotation(supperClass,JSONType.class);
                if(superJsonType == null){
                    break;
                }
                typeKey = superJsonType.typeKey();
                if(typeKey.length() != 0){
                    break;
                }
            }

            for(Class<?> interfaceClass : beanType.getInterfaces()){
                JSONType superJsonType = TypeUtils.getAnnotation(interfaceClass,JSONType.class);
                if(superJsonType != null){
                    typeKey = superJsonType.typeKey();
                    if(typeKey.length() != 0){
                        break;
                    }
                }
            }

            if(typeKey != null && typeKey.length() == 0){
                typeKey = null;
            }
        } else{
            features = 0;
        }
        // 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);
        FieldInfo[] fields = new FieldInfo[fieldInfoList.size()];
        fieldInfoList.toArray(fields);
        FieldInfo[] sortedFields;
        List<FieldInfo> sortedFieldList;
        if(orders != null && orders.length != 0){
            sortedFieldList = fieldBased
                    ? computeGettersWithFieldBase(beanType, aliasMap, true, propertyNamingStrategy) //
                    : computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, true, propertyNamingStrategy);
        } else{
            sortedFieldList = new ArrayList<FieldInfo>(fieldInfoList);
            Collections.sort(sortedFieldList);
        }
        sortedFields = new FieldInfo[sortedFieldList.size()];
        sortedFieldList.toArray(sortedFields);
        if(Arrays.equals(sortedFields, fields)){
            sortedFields = fields;
        }
        return new SerializeBeanInfo(beanType, jsonType, typeName, typeKey, features, fields, sortedFields);
    }

这个方法便是我们要找到的最重要的方法,JSONType jsonType = TypeUtils.getAnnotation(beanType,JSONType.class);这行代码映入我们的眼帘,一切问题都迎刃而解了。大概意思就是,获取class类上的@JSONType 注解信息,若是存在,则根据指定的顺序排序。

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface JSONType {

    boolean asm() default true;

    String[] orders() default {};

    /**
     * @since 1.2.6
     */
    String[] includes() default {};

    String[] ignores() default {};

    SerializerFeature[] serialzeFeatures() default {};
    Feature[] parseFeatures() default {};
    
    boolean alphabetic() default true;
    
    Class<?> mappingTo() default Void.class;
    
    Class<?> builder() default Void.class;
    
    /**
     * @since 1.2.11
     */
    String typeName() default "";

    /**
     * @since 1.2.32
     */
    String typeKey() default "";
    
    /**
     * @since 1.2.11
     */
    Class<?>[] seeAlso() default{};
    
    /**
     * @since 1.2.14
     */
    Class<?> serializer() default Void.class;
    
    /**
     * @since 1.2.14
     */
    Class<?> deserializer() default Void.class;

    boolean serializeEnumAsJavaBean() default false;

    PropertyNamingStrategy naming() default PropertyNamingStrategy.CamelCase;

    /**
     * @since 1.2.49
     */
    Class<? extends SerializeFilter>[] serialzeFilters() default {};

    /**
     * @since 1.2.71
     * @return
     */
    Class<? extends ParserConfig.AutoTypeCheckHandler> autoTypeCheckHandler() default ParserConfig.AutoTypeCheckHandler.class;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值