jackson序列化机制

jackson序列化原理

//BeanSerializer.serialize  166p

    //jackson的序列化就是在这里完成的, 可以debug看一下
    @Override
    public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider)throws IOException{
        if (_objectIdWriter != null) {                            // 初始化操作
            gen.setCurrentValue(bean); // [databind#631]
            _serializeWithObjectId(bean, gen, provider, true);
            return;
        }
                                                                  // 这个gen就是我们需要全程跟踪的char数组, 也是整个序列化的核心
        gen.writeStartObject(bean);                               // write首字符 "{",  不同的序列化器并不局限于json序列化 
        if (_propertyFilterId != null) {                          // 下面就是字段的序列化, 先看一下有无属性配置
            serializeFieldsFiltered(bean, gen, provider);                
        } else {
            serializeFields(bean, gen, provider);                 // 默认走的是这里
        }
        gen.writeEndObject();                                     // write尾字符 "}"
    }

字段序列化过程

//上面的全程中调用了这个, 继续debug跟踪, 看jackson是如何处理的

protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)throws IOException{
    final BeanPropertyWriter[] props;                                        // 定义了一个props数组, 其实是bean的属性数组
    if (_filteredProps != null && provider.getActiveView() != null) {
        props = _filteredProps;
    } else {
        props = _props;
    }                                                                         // 数组直接赋了值, 但是这个值是从哪来的, 可以看后面的
    int i = 0;
    try {
        for (final int len = props.length; i < len; ++i) {
            BeanPropertyWriter prop = props[i];
            if (prop != null) { // can have nulls in filtered list
                prop.serializeAsField(bean, gen, provider);                   // 字段复制, provider其实就是那个全局的gen
            }
        }
        if (_anyGetterWriter != null) {
            _anyGetterWriter.getAndSerialize(bean, gen, provider);
        }
    } catch (Exception e) {
        String name = (i == props.length) ? "[anySetter]" : props[i].getName();
        wrapAndThrow(provider, e, bean, name);
    } catch (StackOverflowError e) {
        DatabindException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e);
        String name = (i == props.length) ? "[anySetter]" : props[i].getName();
        mapE.prependPath(bean, name);
        throw mapE;
    }
}
// 序列化字段的impl
public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
        // 这里就是为什么getter的优先级高于field, 优先取getter中的值
        // _field.get(bean): 是field仅在public访问权限时才能获取到, (这里非public不会报错的, 因为能走到这里的非public属性都是具有getter的属性, 可以看后面的验证)
        final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean, (Object[]) null);
                
        ......
}

属性名称操作顺序

protected void collectAll() {
        LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>();        //props是字段数组,

        _addFields(props);                 // 根据object的字段往 props 数组里赋值, 这个时候是不区分private or public的, 同样也不区分@JsonIgnore注解
        _addMethods(props);                // 根据object的getter方法往 props 数组里赋值
 
        if (!_classDef.isNonStaticInnerClass()) {        //是否静态内部类字段(独有的配置, 在此没有深入研究)
            _addCreators(props);
        }

        _removeUnwantedProperties(props);        //见文知意: 移除unWant的属性, 此时会移除掉@JsonIgnore标注的字段 
                                                 //(@JsonIgnore的作用域是field, Getter, Setter), 即便在序列化时的setter方法上标注,也会去除

        _removeUnwantedAccessor(props);          

        _renameProperties(props);                //属性重命名: (包括json注解 + getter方法覆盖)

        ......

        _sortProperties(props);                //属性排序
        _properties = props;                   //赋值给_properties, _properties会被后面用到
        _collected = true;                        
    }

Getter名称大小写问题(byName?)

//jackson 序列化时get后字段取值的问题
//下面是jakson的源码 (DefaultAccessorNamingStrategy: legacyManglePropertyName()    137)
// @param basename: getter方法名称
// @param offset: get后一位位置, "get".length() (默认序列化器的前缀是"get")
protected String legacyManglePropertyName(final String basename, final int offset){
        final int end = basename.length();
        if (end == offset) {                                     // empty name, no handler
            return null;
        }
        char c = basename.charAt(offset);                        //获取"get"后一位字符, (下面统称 firstChar )
        if (_baseNameValidator != null) {                        //基础name校验规则, 需要自定义配置
            if (!_baseNameValidator.accept(c, basename, offset)) {
                return null;
            }
        }
        char d = Character.toLowerCase(c);                        //将firstChar转为小写
        if (c == d) {                                             
            return basename.substring(offset);                    //如果firstChar本来就是小写, 那么将get截取掉, 后面的全部返回 !!!!!!!!!!
        }                        
        StringBuilder sb = new StringBuilder(end - offset);        //end是字符串终点, 
        sb.append(d);                                              //sb是最终json串中的name最终的值, d当前值
        int i = offset+1;                                          //取get后第二位的位置  
        for (; i < end; ++i) {                                
            c = basename.charAt(i);                                //取 i 位置上的字符(第一次就是get后第二位的位置) c
            d = Character.toLowerCase(c);                          //取c的小写字符  
            if (c == d) {                                          //如果c本来就是小写, 那就把后面的全部追加上去, 并且break      
                sb.append(basename, i, end);        
                break;
            }
            sb.append(d);                                           //如果c是大写, 那么c的小写先追加上去, 再次便利, 直至循环结束  
        }
        return sb.toString();
}
总结:
1.如果firstChar本来就是小写, 那么将get截取掉, 后面的全部返回
2.从第二个开始遍历, 遇到小写则停止并截取; 遇见大写则转为小写



jackson常用的注解生效时间
1. @JsonIgnore (被该注解标示的字段, 不参与序列化)
_removeUnwantedProperties(props); 
由第七条可以看出: @JsonIgnore并非不参与, 而是先加入, 后剔除.
移除机制包括: field, getter, setter (只要有任一标注了@JsonIgnore, 则该字段就会被移除)



2. @JsonProperties (被该注解标示的字段, 覆盖原有的字段名)
_renameProperties(props); 
由第七条可以看出: @JsonProperties标注的属性会被覆盖掉, 
有一点可以注意的是: 被@JsonProperties标注的字段, 往往在序列化的时候总是出现在json串的结尾。这是因为props数组会先删除掉原有字段,再写入新值。
jackson源码: POJOPropertiesCollector._renameProperties(Map<String, POJOPropertyBuilder> props);   956p



3.为什么单字段(无getter)不会进行序列化?
 _removeUnwantedProperties(props); 
在进行remove的时候, 如果一个字段是private且没有getter方法, jackson会认为它是一个不合适的方法, 会把他移除掉.
在进行remove的时候, 如果一个字段是public则不会移除, props里仅有属性名称, 没有属性值.