ObjectMapper中MapperFeature.USE_GETTERS_AS_SETTERS对反序列化的影响
一、依赖版本
jackson-databind-2.8.10
二、MapperFeature.USE_GETTERS_AS_SETTERS的作用
源码解释
/*** Feature that determines whether otherwise regular "getter"* methods (but only ones that handle Collections and Maps,* not getters of other type)* can be used for purpose of getting a reference to a Collection* and Map to modify the property, without requiring a setter* method.* This is similar to how JAXB framework sets Collections and* Maps: no setter is involved, just setter.*<p>* Note that such getters-as-setters methods have lower* precedence than setters, so they are only used if no* setter is found for the Map/Collection property.*<p>* Feature is enabled by default.*/USE_GETTERS_AS_SETTERS(true)
USE_GETTERS_AS_SETTERS开启时,针对Map和Collection类型的属性可以用getter防获取属性的引用来改变属性的值,不需要setter方法。
反序列化:在目标类中,用我们期望的值,修改那些可以被修改的属性值。正常的属性都是通过setter进行属性赋值,而Collection和Map可以直接用getter获取到属性的引用以此直接修改属性值,所以当USE_GETTERS_AS_SETTERS开启时,ObjectMapper认为Map和Collection这两种值,也应当被反序列化。
三、示例
创建一个用户类。
只有一个getAddresses方法,并没有addresses属性
package deserialize;import java.util.ArrayList;
import java.util.List;public class User {private Long id;private String name;public List<String> getAddresses() {List<String> list = new ArrayList<>();list.add("asd");return list;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}
}
序列化时,ObjectMapper会将addresses作为属性加到为JSON里(如果没有addresses属性,我认为完全可以不序列化)。
- 开启MapperFeature.USE_GETTERS_AS_SETTERS(默认开启)进行测试, 代码如下:
@Testpublic void serialize() throws IOException {ObjectMapper objectMapper = new ObjectMapper();// 关闭USE_GETTERS_AS_SETTERS 会报错找不到属性,正常属性用// com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet赋值// 开启时找到属性执行方法就结束,会调用com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet// 该方法执行完结束
// objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS);User user = new User();user.setId(1L);user.setName("zhang");String s = objectMapper.writeValueAsString(user);System.out.println(s);Object value = objectMapper.readValue(s, User.class);System.out.println(value);}
运行结果:
此时,调用ObjectMapper.readValue进行反序列化,正常。
- 关闭MapperFeature.USE_GETTERS_AS_SETTERS(默认开启)进行测试, 代码如下:
@Testpublic void serialize() throws IOException {ObjectMapper objectMapper = new ObjectMapper();// 关闭USE_GETTERS_AS_SETTERS 会报错找不到属性,正常属性用// com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet赋值// 开启时找到属性执行方法就结束,会调用com.fasterxml.jackson.databind.deser.impl.SetterlessProperty.deserializeAndSet// 该方法执行完结束objectMapper.disable(MapperFeature.USE_GETTERS_AS_SETTERS);User user = new User();user.setId(1L);user.setName("zhang");String s = objectMapper.writeValueAsString(user);System.out.println(s);Object value = objectMapper.readValue(s, User.class);System.out.println(value);}
运行结果:
此时,调用ObjectMapper.readValue进行反序列化,抛出异常,无法识别addresses属性。
四、反序列化ObjectMapper.readValue源码分析
1、反序列化过程
com.fasterxml.jackson.databind.ObjectMapper#readValue(java.lang.String, java.lang.Class<T>)
com.fasterxml.jackson.databind.ObjectMapper#_readMapAndClose
// 获取反序列化器
com.fasterxml.jackson.databind.ObjectMapper#_findRootDeserializer
// 进行反序列化
com.fasterxml.jackson.databind.deser.BeanDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
// 反序列化
com.fasterxml.jackson.databind.deser.BeanDeserializer#vanillaDeserialize
private final Object vanillaDeserialize(JsonParser p,DeserializationContext ctxt, JsonToken t)throws IOException{final Object bean = _valueInstantiator.createUsingDefault(ctxt);// [databind#631]: Assign current value, to be accessible by custom serializersp.setCurrentValue(bean);if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {String propName = p.getCurrentName();do {p.nextToken();// MapperFeature.USE_GETTERS_AS_SETTERS 开启时(默认开启), 执行该方法会返回SetterlessProperty对象,无事发生// MapperFeature.USE_GETTERS_AS_SETTERS 关闭时,该值为空 会抛出异常SettableBeanProperty prop = _beanProperties.find(propName);if (prop != null) { // normal casetry {// 反序列化中的_beanProperties存在该属性值,该属性值就会被赋值, 但是只有一个getter没有字段定义,则无事发生prop.deserializeAndSet(p, ctxt, bean);} catch (Exception e) {wrapAndThrow(e, bean, propName, ctxt);}continue;}// 抛出异常handleUnknownVanilla(p, ctxt, bean, propName);} while ((propName = p.nextFieldName()) != null);}return bean;}
遍历目标类User中所有的属性名propName(因为存在getAddresses方法,jackson这里认为包含addresses属性)。如果在com.fasterxml.jackson.databind.deser.BeanDeserializerBase#_beanProperties中存在该属性则进行属性赋值,否则调用com.fasterxml.jackson.databind.deser.BeanDeserializerBase#handleUnknownVanilla方法抛出异常。
这里先放结论:
- MapperFeature.USE_GETTERS_AS_SETTERS开启,_beanProperties中存在addresses的属性,所以程序可以正常执行,但由于在User类中没有该属性,所以就有了上面的反序列化的结果(addresses并没有用)。
- 反之,_beanProperties中不存在addresses的属性,所以会走到handleUnknownVanilla抛出异常
_beanProperties是反序列化器com.fasterxml.jackson.databind.deser.BeanDeserializer的父类com.fasterxml.jackson.databind.deser.BeanDeserializerBase中的属性,下面看反序列化器,是如何创建的。
2、创建反序列化器
com.fasterxml.jackson.databind.ObjectMapper#readValue(java.lang.String, java.lang.Class<T>)
com.fasterxml.jackson.databind.ObjectMapper#_readMapAndClose
// 获取反序列化器
com.fasterxml.jackson.databind.ObjectMapper#_findRootDeserializer
com.fasterxml.jackson.databind.DeserializationContext#findRootValueDeserializer
com.fasterxml.jackson.databind.deser.DeserializerCache#findValueDeserializer
// 创建并缓存反序列化器
com.fasterxml.jackson.databind.deser.DeserializerCache#_createAndCacheValueDeserializer
com.fasterxml.jackson.databind.deser.DeserializerCache#_createAndCache2
// 创建反序列化器
com.fasterxml.jackson.databind.deser.DeserializerCache#_createDeserializer
com.fasterxml.jackson.databind.deser.DeserializerCache#_createDeserializer2
// 创建实例的反序列化器
com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#createBeanDeserializer
// 构建实例的反序列化器
com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#buildBeanDeserializer
// 添加实例的属性到序列化器中,这里添加的属性就是向上文的_beanProperties中赋值
com.fasterxml.jackson.databind.deser.BeanDeserializerFactory#addBeanProps
在addBeanProps方法中有这样一段代码
// 遍历实例的属性定义 只要存在getter属性定义就会有值// At which point we still have all kinds of properties; not all with mutators:for (BeanPropertyDefinition propDef : propDefs) {SettableBeanProperty prop = null;/* 18-Oct-2013, tatu: Although constructor parameters have highest precedence,* we need to do linkage (as per [databind#318]), and so need to start with* other types, and only then create constructor parameter, if any.*/if (propDef.hasSetter()) {JavaType propertyType = propDef.getSetter().getParameterType(0);prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);} else if (propDef.hasField()) {JavaType propertyType = propDef.getField().getType();prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);} else if (useGettersAsSetters && propDef.hasGetter()) {// 如果useGettersAsSetters开启 并且有getter则认为该属性需要被反序列化/* May also need to consider getters* for Map/Collection properties; but with lowest precedence*/AnnotatedMethod getter = propDef.getGetter();// should only consider Collections and Maps, for now?Class<?> rawPropertyType = getter.getRawType();if (Collection.class.isAssignableFrom(rawPropertyType)|| Map.class.isAssignableFrom(rawPropertyType)) {// 构造SetterlessProperty对象prop = constructSetterlessProperty(ctxt, beanDesc, propDef);}}
遍历实例中的属性定义(因为存在getAddresses方法,所以这里有三个值id,name和addresses)。
如果该属性有setter方法或者有该属性,都会构造出一个com.fasterxml.jackson.databind.deser.SettableBeanProperty对象;
如果USE_GETTERS_AS_SETTERS开启,并且有getter方法,并且该属性的类型是Collection或者Map,会构造一个com.fasterxml.jackson.databind.deser.impl.SetterlessProperty对象,以上对象都用prop来保存。如果prop不为空则会将该属性加到反序列化器中的_beanProperties中,到反序列化时,程序会正常运行;当prop为空时,则不会加入到_beanProperties中,到反序列化时,会抛出无法识别属性的异常。
五、结论
对于存在getter方法,而没有对应属性值的类进行序列化时,会将getter方法对应的属性值一起序列化,转成json。
对于存在getter方法的并且getter方法的返回值是Map或者Collection类型的类,进行序列化后,反序列化时,如果开启USE_GETTERS_AS_SETTERS,则会正常运行;如果关闭USE_GETTERS_AS_SETTERS则会抛出异常。
如有错误,欢迎指正。
源码地址:https://gitee.com/zhang-dj/jackson-databind-jackson-databind-2.8.10
针对文章内容自己加了一些注释
ObjectMapper中MapperFeature.USE_GETTERS_AS_SETTERS对反序列化的影响相关推荐
- java 自定义反序列化_java – 使用类字段中指定的自定义反序列化器反序列化字符串...
我需要编写一个方法,它接受一些对象,一些字段名称fieldName存在于给定对象的类中,以及一些字段值.该值是字段的 JSON序列化形式.该方法应取值并相应地反序列化,如下所示: static voi ...
- 开源组件 Ehcache中被曝严重漏洞,影响多款Jira产品
聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士 Atlassian 公司督促企业客户修复其 Jira Data Center 和 Jira Service Management Data ...
- fileinputstream自定义类序列化和反序列化_Rest Assured篇:Java中的序列化和反序列化...
点击上方蓝字设为星标 每天傍晚伴你一起成长! Java 中的序列化和反序列化是一个重要的编程概念.它适用于所有主要的编程语言.在本章中,我们将尝试在Java语言的上下文中理解此概念.在本章的最后,我们 ...
- python中常用的序列化模块_Python中的序列化和反序列化
为什么要序列化 内存中的字典.列表.集合以及各种对象,如何保存到一个文件中. 设计一套协议,按照某种规则,把内存中的数据保存到文件中,文件是一个个字节序列.所以必须把数据额转换为字节序列,输出到文件, ...
- mysql修改索引对交易影响吗_MySQL中字符串索引对update的影响分析
本文分析了mysql中字符串索引对update的影响.分享给大家供大家参考,具体如下: 对某一个类型为varchar的字段添加前缀索引后,基于该子段的条件查询时间基本大幅下降:但对于update操作, ...
- java 不能反序列化_java中的序列化与反序列化
序列化就是将一个对象以及他的属性写入一个文件,保存在存中对象的状态 反序列化就是把保存的对象状态再读出来 实现序列化与反序列化就要使用到IO操作中文件的字节流输入与输出 inputStream与out ...
- 谈谈:.Net中的序列化和反序列化
序列化和反序列化相信大家都经常听到,也都会用, 然而有些人可能不知道:.net为什么要有这个东西以及.net Frameword如何为我们实现这样的机制, 在这里我也是简单谈谈我对序列化和反序列化的一 ...
- java rest 序列化_Django Rest Framework中的序列化和反序列化
作为Django REST Framework的新手,我在给我的ModelSerializer中的主键反序列化对象时遇到了问题 . 例如,我有2个扩展ModelSerializer的序列化程序: cl ...
- 网络中延迟对主观感受的影响
T. Hossfeld等人在论文<INITIAL DELAY VS. INTERRUPTIONS: BETWEEN THE DEVIL AND THE DEEP BLUE SEA>中,研究 ...
最新文章
- SUSE glibc升级为2.18过程记录
- 初学HTML5系列二:HTML5新增的事件属性
- 4.Lucene3.案例介绍,创建索引,查询等操作验证
- 配置 tsconfig.json
- 【转】阿里技术专家详解DDD系列 第二讲 - 应用架构
- Linux内核系统架构介绍
- EBS 报表输出PDF时中文乱码
- SqlServer数据冗余的问题和解决
- android应用开发实验报告_聚焦 Android 11: Android 11 应用兼容性
- 神经网络控制与matlab仿真,matlab神经网络能做什么
- 【MATLAB】 曲面的绘制
- 腾讯文档表格内存优化总结
- MOX:开创区块链通证参与电影融资的新篇章
- 传世私服服务器列表不显示,关于传世私服的人物名字显示设置详解
- QT自定义Widget控件及其使用
- Python中的模块(二)
- ★★★GG口述实录:我和‘老山’英雄的真实接触★★★(PK:★★★MM口述实录:我和程序员老公的幸福生活★★★ )
- 根据PPG估算血压利用频谱谱-时间深度神经网络【翻】
- PHPMyWind编辑器支持pdf自动上传
- proe PTC Pro_Engineer wildfire4.0 M040野火版 DVD32位
热门文章
- PostgreSQL数据库统计信息——compute_scalar_stats计算统计数据
- 开源库muduo学习-总结篇
- 平安iq测试没通过的话影响入职吗_软件测试面试中有哪些潜台词?给你们排雷啦...
- redis ,redisson 分布式锁深入剖析
- openSCA 开 源 啦 !!!
- 使用Swiper插件实现视频轮播,怎么实现切换自动播放视频?
- 手机wps系统打印服务器,MK-WPS101w单USB无线打印服务器网页版教程
- Beyond compare3.3 绿色版+右键菜单修复方法.rar
- 京造k6 68键盘 搜狗输入法冲突
- linux指令-ps