fastjson序列化数据后的字符串是空的?

1,059 阅读3分钟

很少用fastjson,写了个demo准备研究一下源码,奇怪的事情发生了,序列化一个Bean类后的字符串居然是空的,这跟我想的有点不一样

public class Student {
    int id;
    String name;
    int age;

    public static void main(String[] args) {
        Student student = new Student();
        student.id = 100;
        student.name = "lbt";
        student.age = 18;
        String jsonString = JSON.toJSONString(student);
        System.out.println(jsonString);
    }
}

输出结果如下:

{}

带着疑问读了一下源码,从Json.toJSONString(Object object)开始

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

首先初始化一个SerializeWriter对象,接着将该SerializeWriter对象传入JSONSerializer构造器并创建出一个JSONSerializer对象serializer,toJSONString()方法的不同构造器传参数可以知道dateFormat为空,该dateFormat是序列化日期的一种格式;filters也是空的,可在序列化的过程中过滤掉某些变量

接着看serializer.write(object)方法,该方法是序列化过程的核心方法

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

首先获取object的class对象,用于后面通过Class获取方法和全局变量 接着调用getObjectWriter()方法获取ObjectSerializer

public ObjectSerializer getObjectWriter(Class<?> clazz) {
    return config.getObjectWriter(clazz);
}

往下看,执行到SerializeConfig类的getObjectWriter()方法中,该方法很长,看主要的

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

    if (writer != null) {
        return writer;
    }
    if (create) {
        writer = createJavaBeanSerializer(clazz);
        put(clazz, writer);
    }
    return writer;
}

1. 首先get(clazz)从当前缓存中获取writer对象,如果存在writer对象则返回该writer,否则执行下一步

2. 调用createJavaBeanSerializer(clazz)对象并赋值给writer

接着看createJavaBeanSerializer()方法

   public final ObjectSerializer createJavaBeanSerializer(Class<?> clazz) {
    String className = clazz.getName();
    long hashCode64 = TypeUtils.fnv1a_64(className);
    if (Arrays.binarySearch(denyClasses, hashCode64) >= 0) {
        throw new JSONException("not support class : " + className);
       }

    SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy, fieldBased);
    if (beanInfo.fields.length == 0 && Iterable.class.isAssignableFrom(clazz)) {
        return MiscCodec.instance;
    }

    return createJavaBeanSerializer(beanInfo);
}

接下来通过TypeUtils.buildBeanInfo()方法创建SerializeBeanInfo对象,buildBeanInfo()也很长,看主要方法

public static SerializeBeanInfo buildBeanInfo(Class<?> beanType //
        , Map<String,String> aliasMap //
        , PropertyNamingStrategy propertyNamingStrategy //
        , boolean fieldBased //){
    List<FieldInfo> fieldInfoList = fieldBased
            ? computeGettersWithFieldBase(beanType, aliasMap, false, propertyNamingStrategy) //
            : computeGetters(beanType, jsonType, aliasMap, fieldCacheMap, false, propertyNamingStrategy);
    return new SerializeBeanInfo(beanType, jsonType, typeName, typeKey, features, fields, sortedFields);
}

调用computeGetters()方法,将序列化的类的FieldInfo存入List中

public static List<FieldInfo> computeGetters(Class<?> clazz, //
                                             JSONType jsonType, //
                                             Map<String,String> aliasMap, //
                                             Map<String,Field> fieldCacheMap, //
                                             boolean sorted, //
                                             PropertyNamingStrategy propertyNamingStrategy //
){
    Method[] methods = clazz.getMethods();
    for(Method method : methods){
        if(Modifier.isStatic(method.getModifiers())){
            continue;
        }
        Class<?> returnType = method.getReturnType();
        if(returnType.equals(Void.TYPE)){
            continue;
        }
        if(method.getParameterTypes().length != 0){
            continue;
        }
        
        if(methodName.startsWith("get")){
            if(methodName.length() < 4){
                continue;
            }
            if(methodName.equals("getClass")){
                continue;
            }
            if(methodName.equals("getDeclaringClass") && clazz.isEnum()){
                continue;
            }
            char c3 = methodName.charAt(3);
            String propertyName;
            Field field = null;
            if(Character.isUpperCase(c3) //
                    || c3 > 512 // for unicode method name
            ){
                if(compatibleWithJavaBean){
                    propertyName = decapitalize(methodName.substring(3));
                } else{
                    propertyName = TypeUtils.getPropertyNameByMethodName(methodName);
                }
                propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName, 3);
            }

            FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
                    annotation, fieldAnnotation, label);
            fieldInfoMap.put(propertyName, fieldInfo);
        }
        
        if(methodName.startsWith("is")){
            if(methodName.length() < 3){
                continue;
            }
            if(returnType != Boolean.TYPE
                    && returnType != Boolean.class){
                continue;
            }
            char c2 = methodName.charAt(2);
            String propertyName;
            Field field = null;
            if(Character.isUpperCase(c2)){
                if(compatibleWithJavaBean){
                    propertyName = decapitalize(methodName.substring(2));
                } else{
                    propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
                }
                propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName, 2);
            }
            FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
                    annotation, fieldAnnotation, label);
            fieldInfoMap.put(propertyName, fieldInfo);
        }

    }
    
    Field[] fields = clazz.getFields();
    computeFields(clazz, aliasMap, propertyNamingStrategy, fieldInfoMap, fields);
    return getFieldInfos(clazz, sorted, fieldInfoMap);
}

1. 首先遍历类中的public方法,找到方法名称以get或者is开头的方法,获取方法对应的变量对象名称propertyName,并将其存入Map中

2.接着获取Field对象数组并且调用 computeFields()方法滤除掉static属性变量,以及在遍历method获取到的变量,将其余的变量存入HashMap中

public Field[] getFields() throws SecurityException {
    List<Field> fields = new ArrayList<Field>();
    getPublicFieldsRecursive(fields);
    return fields.toArray(new Field[fields.size()]);
}

接着看getPublicFieldsRecursive()方法,该方法会调用getPublicDeclaredFields()方法,获取该类、父类和接口中的所有的public属性方法

private void getPublicFieldsRecursive(List<Field> result) {
    // search superclasses
    for (Class<?> c = this; c != null; c = c.superClass) {
        Collections.addAll(result, c.getPublicDeclaredFields());
    }

    // search iftable which has a flattened and uniqued list of interfaces
    Object[] iftable = ifTable;
    if (iftable != null) {
        for (int i = 0; i < iftable.length; i += 2) {
            Collections.addAll(result, ((Class<?>) iftable[i]).getPublicDeclaredFields());
        }
    }
}

结论:

由此可以看出,如果一个Bean类没有实现public修饰的get、is等方法获取变量,以及变量的可见性为非public,那么这些变量均不会参与序列化

理由也很简单,因为对象序列化后必然要经过反序列化,反序列化过程需要对变量进行赋值,如果是非public变量,而且没有public修饰的set、get方法,是无法给变量赋值,因此序列化只序列化能够设置值得变量