JsonObject使用浅析

       最近在进行接口联调时,频繁使用LomBok@Data,BeanUtils以及fastjson,因此来总结学习一下。在进行web业务开发时,会频繁的将Bean与Json字符串进行转换,其中在使用JSONObject.toJSONString(bean)来进行转换时,发现转换成的json中出现字段缺失,后来研究了一下,原来是JSONObject.toJSONString(bean)默认会将bean中值为null的过滤掉,可以使用SerializerFeature属性来控制转换规则。

准备一些测试类

public enum EnumT {

    Tom("tom", 13),
    Jack("jack", 18);

    EnumT(String name, int age) {}
}
@Data
public class Dog {

    private Integer size;

    private String name;

    public Dog(Integer size, String name) {
        this.size = size;
        this.name = name;
    }
}
@Data
public class User {

    private Integer age;

    private String name;

    private boolean leader;

    private List<Dog> logList;

    private EnumT enumT;

    private Date birth;

    public User(Integer age, String name, List<Dog> logList,
            boolean leader, EnumT enumT, Date birth) {
        this.age = age;
        this.name = name;
        this.logList = logList;
        this.leader = leader;
        this.enumT = enumT;
        this.birth = birth;
    }

    /**
     * You can annotate any field with @Getter and/or @Setter, to let lombok generate the default getter/setter automatically.
     * A default getter simply returns the field, and is named getFoo if the field is called foo (or isFoo if the field's type is boolean).
     * A default setter is named setFoo if the field is called foo, returns void, and takes 1 parameter of the same type as the field.
     * It simply sets the field to this value.
     *
     */
}

Bean转换为Json对象

public class JsonTest {

    public static void main(String[] args) {
        List<Dog> dogList = new ArrayList<>();
        for(int i=0; i<3; i++){
            dogList.add(new Dog(i, "name"+i));
            //dogList.add(new Dog(i, null));
        }

        User user = new User(18, "", dogList, false, EnumT.Jack, new Date());
        System.out.println(user.toString());

        //对象转为String,默认情况下会忽略值为Null的key-value对
        String s1 = JSONObject.toJSONString(user);
        System.out.println(s1);

        //是否输出值为null的字段,默认为
        //String s2 = JSONObject.toJSONString(user, SerializerFeature.WRITE_MAP_NULL_FEATURES);

        输出key时是否使用双引号,默认为true
        //String s2 = JSONObject.toJSONString(user, SerializerFeature.QuoteFieldNames);

        //使用单引号而不是双引号,默认为false
        //String s2 = JSONObject.toJSONString(user, SerializerFeature.UseSingleQuotes);

        //Enum输出name()或者original,默认为false
        //String s2 = JSONObject.toJSONString(user, SerializerFeature.WriteEnumUsingToString);

        //Date使用ISO8601格式输出,默认为false
        //String s2 = JSONObject.toJSONString(user, SerializerFeature.UseISO8601DateFormat);

        //按字段名称排序后输出。默认为false
        String s2 = JSONObject.toJSONString(user, SerializerFeature.SortField);

        System.out.println(s2);

        //将String转化为对象
        //User u = JSONObject.parseObject(s2, User.class);
        //System.out.println(u.toString());

    }
}

如上所示,可以使用SerializerFeature来控制具体的转换规则

                                          

其具体的控制规则如下所示:
QuoteFieldNames    输出key时是否使用双引号,默认为true    
UseSingleQuotes    使用单引号而不是双引号,默认为false    
WriteMapNullValue    是否输出值为null的字段,默认为false    
WriteEnumUsingToString    Enum输出name()或者original,默认为false    
UseISO8601DateFormat    Date使用ISO8601格式输出,默认为false    
WriteNullListAsEmpty    List字段如果为null,输出为[],而非null    
WriteNullStringAsEmpty    字符类型字段如果为null,输出为”“,而非null    
WriteNullNumberAsZero    数值字段如果为null,输出为0,而非null    
WriteNullBooleanAsFalse    Boolean字段如果为null,输出为false,而非null    
SkipTransientField    如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true    
SortField    按字段名称排序后输出。默认为false    
WriteTabAsSpecial    把\t做转义输出,默认为false    不推荐
PrettyFormat    结果是否格式化,默认为false    
WriteClassName    序列化时写入类型信息,默认为false。反序列化是需用到    
DisableCircularReferenceDetect    消除对同一对象循环引用的问题,默认为false    
WriteSlashAsSpecial    对斜杠’/’进行转义    
BrowserCompatible    将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false    
WriteDateUseDateFormat    全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);    

toJSONString方法源码简析

public static String toJSONString(Object object, // 
                                      SerializeConfig config, // 
                                      SerializeFilter[] filters, // 
                                      String dateFormat, //
                                      int defaultFeatures, // 
                                      SerializerFeature... features) {
        SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);

        try {
            JSONSerializer serializer = new JSONSerializer(out, config);
            
            if (dateFormat != null && dateFormat.length() != 0) {
                serializer.setDateFormat(dateFormat);
                serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
            }

            if (filters != null) {
                for (SerializeFilter filter : filters) {
                    serializer.addFilter(filter);
                }
            }

            serializer.write(object);

            return out.toString();
        } finally {
            out.close();
        }
    }

传入待转换的Bean以及各种配置项,通过JSONSerializer的write方法来转换输出json

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

由于带转换的bean可能是个复杂类型,因此需要先获取bean的Class对象信息,这样便可以利用反射来分析bean中属性类型等信息。ObjectSerializer为一个核心接口,定义了某种类型的具体转换输出实现

public interface ObjectSerializer {
    
    /**
     * fastjson invokes this call-back method during serialization when it encounters a field of the
     * specified type.
     * @param serializer 
     * @param object src the object that needs to be converted to Json.
     * @param fieldName parent object field name
     * @param fieldType parent object field type
     * @param features parent object field serializer features
     * @throws IOException
     */
    void write(JSONSerializer serializer, //
               Object object, //
               Object fieldName, //
               Type fieldType, //
               int features) throws IOException;
}

其中一部分默认实现如下:

       

这里以Boolean类型为例:

如果判断object为boolean类型,则根据value值来输出true或者false

public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        SerializeWriter out = serializer.out;

        Boolean value = (Boolean) object;
        if (value == null) {
            out.writeNull(SerializerFeature.WriteNullBooleanAsFalse);
            return;
        }

        if (value.booleanValue()) {
            out.write("true");
        } else {
            out.write("false");
        }
    }

其中SerializeWriter用来实现最终json字符串的保存,JSONSerializer实现根据Object类型将object转换输出到SerializeWriter中。

public final class SerializeWriter extends Writer {
    private final static ThreadLocal<char[]> bufLocal         = new ThreadLocal<char[]>();
    private final static ThreadLocal<byte[]> bytesBufLocal    = new ThreadLocal<byte[]>();
    private static       int                 BUFFER_THRESHOLD = 1024 * 128;

    static {
        try {
            String prop = IOUtils.getStringProperty("fastjson.serializer_buffer_threshold");
            if (prop != null && prop.length() > 0) {
                int serializer_buffer_threshold = Integer.parseInt(prop);
                if (serializer_buffer_threshold >= 64 && serializer_buffer_threshold <= 1024 * 64) {
                    BUFFER_THRESHOLD = serializer_buffer_threshold * 1024;
                }
            }
        } catch (Throwable error) {
            // skip
        }
    }

    protected char                           buf[];

其中buf[]用来存储最终的json字符串。

研究一下如何根据class对象来获取对应的ObjectSerializer

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

核心方法getObjectWriter

private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
        ObjectSerializer writer = get(clazz);

首先尝试从缓存中获取相应ObjectSerializer,

public final ObjectSerializer get(Type type) {
        Type mixin = JSON.getMixInAnnotations(type);
        if (null == mixin) {
            return this.serializers.get(type);
        }
        IdentityHashMap<Type, ObjectSerializer> mixInClasses = this.mixInSerializers.get(type);
        if (mixInClasses == null) {
            return null;
        }
        return mixInClasses.get(mixin);
    }

如果没有,则循环判断该类型为何种类型,每一种类型都相应处理

if (writer == null) {
            String className = clazz.getName();
            Class<?> superClass;

            if (Map.class.isAssignableFrom(clazz)) {
                put(clazz, writer = MapSerializer.instance);
            } else if (List.class.isAssignableFrom(clazz)) {
                put(clazz, writer = ListSerializer.instance);
            } else if (Collection.class.isAssignableFrom(clazz)) {
                put(clazz, writer = CollectionCodec.instance);
            } else if (Date.class.isAssignableFrom(clazz)) {
                put(clazz, writer = DateCodec.instance);
            } else if (JSONAware.class.isAssignableFrom(clazz)) {
                put(clazz, writer = JSONAwareSerializer.instance);
            } else if (JSONSerializable.class.isAssignableFrom(clazz)) {
                put(clazz, writer = JSONSerializableSerializer.instance);
            } else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
                put(clazz, writer = MiscCodec.instance);
            } else if (clazz.isEnum()) {

整个处理过程很复杂,涉及到底层class对象的处理,总结一下JSONObject.toJSONString

SerializeWriter负责保存转换输出的字符串

JSONSerializer负责根据Bean类型来进行相应处理转换

ObjectSerializer具体类型转换输出的接口定义


Json对象转换为Bean

传入待转换字符串,传入Bean class

User u = JSONObject.parseObject(s2, User.class);
System.out.println(u.toString());

解析字符串方法底层封装方法

之后好好研究一下编译原理,下面是对于特定数据结构进行解析

public static <T> T parseObject(String input, Type clazz, ParserConfig config, ParseProcess processor,
                                          int featureValues, Feature... features) {
        if (input == null || input.isEmpty()) {
            return null;
        }

        if (features != null) {
            for (Feature feature : features) {
                featureValues |= feature.mask;
            }
        }

        DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);

        if (processor != null) {
            if (processor instanceof ExtraTypeProvider) {
                parser.getExtraTypeProviders().add((ExtraTypeProvider) processor);
            }

            if (processor instanceof ExtraProcessor) {
                parser.getExtraProcessors().add((ExtraProcessor) processor);
            }

            if (processor instanceof FieldTypeResolver) {
                parser.setFieldTypeResolver((FieldTypeResolver) processor);
            }
        }

        T value = (T) parser.parseObject(clazz, null);

        parser.handleResovleTask(value);

        parser.close();

        return (T) value;
    }

其中Feature可以控制解析规则

构造解析器

DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);

构造解析器时,判断字符串最外层是一个json对象{}还是json数组[]

public DefaultJSONParser(final Object input, final JSONLexer lexer, final ParserConfig config){
        this.lexer = lexer;
        this.input = input;
        this.config = config;
        this.symbolTable = config.symbolTable;

        int ch = lexer.getCurrent();
        if (ch == '{') {
            lexer.next();
            ((JSONLexerBase) lexer).token = JSONToken.LBRACE;
        } else if (ch == '[') {
            lexer.next();
            ((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
        } else {
            lexer.nextToken(); // prime the pump
        }
    }

对于整个解析器进行可选的规则配置之后,进行解析

T value = (T) parser.parseObject(clazz, null);

解析过程,对于json中字符可能是char或者string或者嵌套json,分情况进行处理

public <T> T parseObject(Type type, Object fieldName) {
        int token = lexer.token();
        if (token == JSONToken.NULL) {
            lexer.nextToken();
            return null;
        }

        if (token == JSONToken.LITERAL_STRING) {
            if (type == byte[].class) {
                byte[] bytes = lexer.bytesValue();
                lexer.nextToken();
                return (T) bytes;
            }

            if (type == char[].class) {
                String strVal = lexer.stringVal();
                lexer.nextToken();
                return (T) strVal.toCharArray();
            }
        }

        ObjectDeserializer deserializer = config.getDeserializer(type);

        try {
            if (deserializer.getClass() == JavaBeanDeserializer.class) {
                if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
                throw new JSONException("syntax error,except start with { or [,but actually start with "+ lexer.tokenName());
            }
                return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
            } else {
                return (T) deserializer.deserialze(this, type, fieldName);
            }
        } catch (JSONException e) {
            throw e;
        } catch (Throwable e) {
            throw new JSONException(e.getMessage(), e);
        }
    }
ObjectDeserializer用来实现特定类型的反序列化

获取某个类型的ObjectDeserializer,如果某个类型是JavaBean简单类型,说明可以直接反序列化,如果不是则需要继续分解

ObjectDeserializer deserializer = config.getDeserializer(type);

        try {
            if (deserializer.getClass() == JavaBeanDeserializer.class) {
                if (lexer.token()!= JSONToken.LBRACE && lexer.token()!=JSONToken.LBRACKET) {
                throw new JSONException("syntax error,except start with { or [,but actually start with "+ lexer.tokenName());
                }
                return (T) ((JavaBeanDeserializer) deserializer).deserialze(this, type, fieldName, 0);
            } else {
                return (T) deserializer.deserialze(this, type, fieldName);
            }
        } catch (JSONException e) {
            throw e;
        } catch (Throwable e) {
            throw new JSONException(e.getMessage(), e);
        }

判定某种具体类型,其具体解析是由JavaBeanDeserializer中方法deserialze实现的

protected <T> T deserialze(DefaultJSONParser parser, // 
                           Type type, // 
                           Object fieldName, // 
                           Object object, //
                           int features, //
                           int[] setFlags) {
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值