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里仅有属性名称, 没有属性值.