本文主要介绍实例解析json反序列化的ObjectMapper,以及Json自定义序列化的方法。有需要的朋友可以了解一下。
对于服务器端开发人员来说,调用第三方接口获取数据,将其转换为代理并返回给客户端几乎是家常便饭。一般第三方接口返回的数据类型是json格式,而服务器开发者需要将json格式的数据转换成对象,然后进行处理封装返回给客户端。
当效率不是特别需要考虑的时候(thrift和protobuffer可以用于搜索、缓存等。),我们通常选择jackson包中的ObjectMapper类对json字符串进行反序列化,得到相应的对象。通常选择readvalue (string content,classvaluetype)方法进行反序列化。
ObjectMapper的readValue方法将json字符串反序列化为对象,如下所示:根据传入的json字符串和目标对象类型创建json parse和JavaType,然后生成DeserializationConfig、DeserializationContext和JsonDeserializer。JsonDeserializer的实现类决定了解析的类型(Bean、Map、String等。)将被执行。JsonParse存储要解析的字符串和其他信息。在解析的过程中,通过token判断当前匹配的类型(比如遇到{,判断为对象类型的起始位置;当遇到[,判断为set类型的起始位置]时,一旦类型确定,就跳转到对应的反序列化类进行处理,得到结果,然后令牌移回来,再解析下一个字符串。它可以被视为递归解析。当它被token判断为对象时,会跳转到BeanDeserializer进行解析,然后遍历对象的所有字段。如果字段是字符串,会跳转到StringDeserializer进行解析;如果字段是数组,它将跳转到CollectionDeserializer中进行解析,直到解析完整个字符串。也可以看做是一种相似性,是对树的深度遍历,相当好理解。
下面简单介绍一下ObjectMapper的readValue方法的反序列化过程:
a:通过json串和对象类型得到JsonParser和JavaType。
public T T readValue(字符串内容,ClassT valueType)
抛出IOException、JsonParseException、JsonMappingException
{
return(T)_ readMapAndClose(_ JSON factory . create parser(content),_ type factory . construct type(value type));
}
//获取json解析器,其中包含解析后的字符串
公共JsonParser createParser(字符串内容)引发IOException,JsonParseException {
final int strLen=content . length();
//实际上,让我们将它用于中等大小的内容,最多64kB块(32kb字符)
if (_inputDecorator!=null || strLen0x8000 ||!canUseCharArrays()) {
//仅包装在读取器中比扩展InputDecorator更容易;或者,如果内容
//太长了,我们无法复制
返回create parser(new string reader(content));
}
io context ctxt=_ create context(content,true);
char[]buf=ctxt . alloctokenbuffer(strLen);
content.getChars(0,strLen,buf,0);
return _createParser(buf,0,strLen,ctxt,true);
}
//将要解析的类型转换为JavaType类型
公共JavaType构造类型(类型类型){
return _constructType(类型,null);
}
受保护的JavaType _constructType(类型类型,类型绑定上下文)
{
JavaType resultType
//简单类?
if (type instanceof Class?) {
resultType=_fromClass((Class?)类型、上下文);
}
//但如果不是,需要开始解析。
else if(type instance of parameterized type){
result type=_ from param type((parameterized type)类型,上下文);
}
else if(type instance of JavaType){//[问题#116]
return (JavaType)类型;
}
else if(type instance of genericaraytype){
result type=_ from array type((genericaraytype)类型,上下文);
}
TypeVariable的type instanceof?) {
result type=_ from variable((type variable?)类型、上下文);
}
else if(通配符类型的类型实例){
结果类型=_通配符((通配符类型)类型,上下文);
}否则{
//健全性检查
抛出新的IllegalArgumentException(无法识别的类型:((type==null)?“[null]”:类型。tostring()));
}
如果(_修饰符!=null!resultType.isContainerType()) {
for(类型修饰符mod:_ modifiers){
结果类型=mod。修改类型(结果类型、类型、上下文、this);
}
}
返回结果类型;
}
b、获取反序列化配置对象和上下文对象,进行第一步的序列化操作。
protected Object _ readMapAndClose(JsonParser jp,JavaType valueType)
抛出IOException、JsonParseException、JsonMappingException
{
尝试{
对象结果;
反序列化配置CFG=getdeserizationconfig();
反序列化上下文ctxt=createdisationcontext(jp,CFG);
//依据值类型得到反序列化的解析器
//对象对应的是豆反序列化程序映射对应的是映射反序列化程序
jsondeserializer对象deser=_ findRootDeserializer(ctxt,值类型);
if (cfg.useRootWrapping()) {
result=_ unwandeserialize(jp,ctxt,cfg,valueType,deser);
}否则{
//如果是对象,则调到豆解串器类中进行解析
result=deser.deserialize(jp,ctxt);
}
ctxt。checkunresolvedobjectid();
}
//也需要使用令牌
jp。clearcurrenttoken();
返回结果;
}最后{
尝试{
jp。close();
} catch (IOException ioe) { }
}
}
c、跳入到BeanDeserializer类中。
下面以豆解串器为例进行讲解:
@覆盖
公共对象反序列化(JsonParser p,反序列化上下文ctxt)
抛出IOException
{
JsonToken t=p . getcurrenttoken();
//常见情况优先
if (t==JsonToken .START_OBJECT) { //TODO:在2.6中,使用p.hasTokenId()
if (_vanillaProcessing) {
返回vanillaDeserialize(p,ctxt,p . nexttoken());
}
p。nexttoken();
if(_对象阅读器!=null) {
返回deserializeWithObjectId(p,ctxt);
}
返回deserializeFromObject(p,ctxt);
}
return _deserializeOther(p,ctxt,t);
}
/**
*简化版本,仅在没有"特殊"时使用
*功能已启用。
*/
私有最终对象vanillaDeserialize(JsonParser p,
反序列化上下文ctxt,JsonToken t)
抛出IOException
{
最终对象bean=_ value实例化器。createusingdefault(ctxt);
//[databind#631]:分配可由自定义序列化程序访问的当前值
p。setcurrentvalue(bean);
for(;t==JsonToken .FIELD _ NAMEt=p.nextToken()) {
string propName=p . getcurrentname();
p。nexttoken();
如果(!_ bean属性。findeserialdandset(p,ctxt,bean,propName)) {
handleUnknownVanilla(p,ctxt,bean,propName);
}
}
回豆;
}
/**
*尝试查找具有给定名称的属性的便利方法,以及
*如果找到,请调用{ @ link SettableBeanProperty # deserialidandset }
*开启它,并返回真实的或者,如果没有找到,则返回错误.
*还要注意,如果尝试反序列化,可能会出现异常
*在必要时被包装,因此调用方不需要处理它们。
*
* @从2.5开始
*/
公共布尔findeserialdandset(JSON解析器p,反序列化上下文ctxt,
对象豆子,字符串键)抛出IOException
{
如果(_不区分大小写){
钥匙=钥匙。tolowercase();
}
int index=key。hashcode()_哈希掩码;
bucket bucket=_ buckets[index];
//让我们展开第一次查找,因为它在90 %的情况下为空或匹配
if (bucket==null) {
返回错误的
}
//主要我们只做身份比较,因为密钥应该被保留
if (bucket.key==key) {
尝试{
水桶。价值。deserialidandset(p,ctxt,bean);
} catch(异常e) {
wrapAndThrow(e,bean,key,ctxt);
}
返回真实的
}
return _ findeserialidandset 2(p,ctxt,bean,key,index);
}
方法属性
@覆盖
public void deserialidandset(JsonParser jp,DeserializationContext ctxt,
对象实例)引发IOException
{
Object value=deserialize(jp,ctxt);
尝试{
//将得到的结果放入反序列化对应的对象中
_setter.invoke(实例,值);
} catch(异常e) {
_throwAsIOE(e,value);
}
}
公共最终对象反序列化抛出IOException
{
JsonToken t=p . getcurrenttoken();
if (t==JsonToken .VALUE_NULL) {
return (_nullProvider==null)?null:_ null提供程序。空值(ctxt);
}
if (_valueTypeDeserializer!=null) {
return _ value deserialir。deserialiwithtype(p,ctxt,_ valueTypeDeserializer
}
return _ value反序列化器。反序列化(p,ctxt);
}
//如果继承了JsonDeserializer类重写了沙漠方法,则会跳转到对应注入的类中进行处理
//不出意外的话最后都会调用反序列化上下文的readValue(JsonParser p,ClassT类型)方法,然后会根据类型的类型跳转到对应的反序列化类中进行处理。
public T T readValue(JsonParser p,ClassT类型)抛出IOException {
返回readValue(p,getTypeFactory().构造类型(type));
}
@SuppressWarnings(未选中)
public T T readValue(JsonParser p,JavaType类型)抛出IOException {
//得到最终解析的类型,映射列表字符串。
jsondeserializer对象deser=findRootValueDeserializer(类型);
if (deser==null) {
}
return (T) deser.deserialize(p,this);
}
//例如这里如果是一个地图,则会调用映射反序列化程序的使沙漠化方法得到最后的返回结果。
//对于集合类,会通过代币按照顺序解析生成一个个的集合对象并放入集合中。
JsonToken t;
while ((t=p.nextToken())!=JsonToken .END_ARRAY) {
尝试{
对象值;
if (t==JsonToken .VALUE_NULL) {
值=值des。getnullvalue();
} else if (typeDeser==null) {
value=valueDes.deserialize(p,ctxt);
}否则{
值=值des。deserializewithtype(p,ctxt,typeDeser);
}
if (referringAccumulator!=null) {
referringAccumulator.add(值);
}否则{
结果.添加(值);
}
} catch(UnresolvedForwardReference引用){
if(referringAccumulator==null){
抛出JsonMappingException。来自(p,"未解析的前向引用,但没有标识信息",引用);
}
引用ref=referringaccumulator。handleunresolvedreference(引用);
reference.getRoid().追加引用(ref);
} catch(异常e) {
抛出jsonmappingexception。wrapwithpath(e,result,result。size());
}
}
返回结果;
在不同的业务场景下,第三方接口返回的数据类型可能会发生变化,比如最初第三方业务代码是使用服务器端编程语言(专业超文本预处理器的缩写)实现的,而与之对接的服务器端也是用服务器端编程语言(专业超文本预处理器的缩写)实现的。后来,又成立了以Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)为开发语言的服务器端开发小组,此时,对接第三方可能会出现问题。第三方返回数据类型的不唯一性,可能会使Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)开发人员无法"正常"反序列化第三方接口返回的json串。例如:第三方接口返回的字段中,当字段为空时,返回的是数组;而字段不为空时,返回的却是对象。这样,那么通过对象映射器进行解析时,就会抛出异常,导致服务器端无法正常将数据返回给客户端。面对这样的问题,可能有以下两种解决方法:
第一种解决方法是对豆中每个字段设置方法内进行判断,当解析字符串是一个数组时,则返回空对象;
当解析的字符串不为空时,就会特别的麻烦,默认情况下,会将Json串解析成一个地图,其中键为豆中字段的名称,值为豆的值。这样,就需要创建一个新的豆子,随后依次从地图中取出对应字段的值,然后再设置到豆中。显然,这种方式很麻烦,一旦第三方字段发生变化时,需要不停地维护这段代码。
第二种解决方法是继承JsonDeserialize,并重写反序列化方法。通过源码可知,JsonDeserializer抽象类是处理反序列化的类,只需在豆类中的字段上加入注解@JsonDeserialize(使用=xxx.class),并且xxx类要继承JsonDeserializer类,且重新对应的反序列化方法,在该方法中进行相应处理即可。在该方法中处理待反序列化字段可能出现的多种不同情况,详情见源码。
这里需要注意的是:当反序列化字段是一个对象,而第三方返回的数据为一个数组时,在重写反序列化方法时,如果判断出当前代币指向的是一个数组,而此时需得到空对象。此时,不能直接返回空对象,必须调用读取值方法,目的是将代币移动到正确的位置,否则,将创建一些奇怪的对象。
对于第二种解决方法,下面举例说明:
包com.string
导入Java。util。地图;
导入com。更快的XML。杰克逊。数据绑定。注释。JSON反序列化;
公共类注释{
公共字符串id;
@ JsonDeserialize(使用=ImgPackSerializer.class)
公共映射字符串,字符串imgPack
@ JsonDeserialize(使用=CoopSerializer.class)
公共合作社;
public Coop getCoop() {
返回笼子
}
public void setCoop(Coop coop) {
this.coop=coop
}
公共映射字符串,字符串getimpack(){
返回imgPack
}
public void setImgPack(MapString,String imgPack) {
这个。img包=img包;
}
公共字符串getId() {
返回id;
}
公共void集合id(字符串id) {
this.id=id
}
}
类别合作
公共整数年龄;
公共整数getAge() {
回归年龄;
}
公共空的存储(整数年龄){
this.age=年龄;
}
}
包com.string
导入Java。io。io异常;
导入Java。util。列表;
导入Java。util。地图;
导入com。更快的XML。杰克逊。核心。JSON解析器;
导入com。更快的XML。杰克逊。核心。jsonprocessingexception
导入com。更快的XML。杰克逊。核心。jsontoken
导入com。更快的XML。杰克逊。数据绑定。沙漠化语境;
导入com。更快的XML。杰克逊。数据绑定。JSON反序列化器;
导入com。更快的XML。杰克逊。数据绑定。对象映射器;
公共类TestJson {
静态对象映射器OBJECT _ MAPPER=新对象映射器();
公共静态void main(String[] args) {
字符串s={code:1 , comm :[{ img pack :{ ABC : ABC }, coop:[]}], name : car } ;
尝试{
响应读取值=对象映射器。读取值,响应。类);
系统。呃。println(读取值。tostring());
} catch (IOException e) {
e。printstacktrace();
}
}
}
类别响应{
公共字符串代码;
公共列表comm comm
公共字符串名称;
公共字符串getName() {
返回名称;
}
公共void集合名称(字符串名){
this.name=name
}
公共字符串getCode() {
返回代码;
}
公共void setCode(字符串代码){
this.code=代码
}
public ListComment getComm() {
返回通信
}
public void setComm(列表注释comm){
this.comm=comm
}
}
类合作串行器扩展了JsonDeserializerCoop {
@覆盖
公共合作反序列化(JsonParser jp,反序列化上下文ctxt)
引发IOException,JsonProcessingException {
JsonToken当前令牌=jp。get current token();
if (currentToken==JsonToken .开始_数组){
//返回null//错误可能会创建更多对象
//jp。nexttoken();//错误
return ctxt.readValue(jp,Object.class)==null?null:null;
} else if(current token==JsonToken .开始_对象){
return (Coop) ctxt.readValue(jp,Coop。类);
}
返回空
}
}
ImgPackSerializer类扩展JsonDeserializerMapString,String {
@覆盖
公共映射字符串,字符串反序列化(JsonParser jp,
反序列化上下文ctxt)抛出IOException,
JsonProcessingException {
JsonToken当前令牌=jp。get current token();
if (currentToken==JsonToken .开始_数组){
return ctxt.readValue(jp,Object.class)==null?null:null;
} else if(current token==JsonToken .开始_对象){
return (MapString,String) ctxt.readValue(jp,map。类);
}
返回空
}
}
总结
以上就是本文关于实例解析Json反序列化之对象映射器(自定义实现反序列化方法)的全部内容,希望对大家有所帮助。欢迎大家参阅本站其他专题,有什么问题可以留言,小编会及时回复大家的。