实例解析Json反序列化之ObjectMapper(自定义实现反序列化方法)
对于服务器端开发人员而言,调用第三方接口获取数据,将其“代理”转化并返给客户端几乎是家常便饭的事儿。 一般情况下,第三方接口返回的数据类型是json格式,而服务器开发人员则需将json格式的数据转换成对象,继而对其进行处理并封装,以返回给客户端。
在不是特别考虑效率的情况下(对于搜索、缓存等情形可以考虑使用thrift和protobuffer),通常我们会选取jackson包中的ObjectMapper类对json串反序列化以得到相应对象。通常会选取readValue(Stringcontent,Class
ObjectMapper的readValue方法将json串反序列化为对象的过程大致为:依据传入的json串和目标对象类型分别创建JsonParse和JavaType,随后生成DeserializationConfig、DeserializationContext、JsonDeserializer,其中JsonDeserializer的实现类决定将要执行哪一种类型解析(Bean、Map、String等),JsonParse中存储了待解析字符串及其它信息,在解析的过程中通过token来判断当前匹配的类型(例如:如果遇到{,将其判断为对象类型的起始位置;遇到[,将其判断为集合类型的起始位置),一旦确定了类型,则跳入与之对应的反序列化类中进行处理,得到结果,然后token往后移动,接着解析下一个串。可以看做类似递归的方式进行解析,当通过token判断为一个对象时,则会跳入BeanDeserializer中进行解析,随后遍历该对象的所有字段,如果字段是字符串,则跳到StringDeserializer中进行解析,如果字段是数组,则跳到CollectionDeserializer中进行解析,直到解析完整个字符串为止。也可以看做类似而树的深度遍历,理解起来还是挺容易的。
下面将简单介绍ObjectMapper的readValue方法进行反序列化的过程:
a:通过json串和对象类型得到JsonParser和JavaType。
publicTreadValue(Stringcontent,Class valueType) throwsIOException,JsonParseException,JsonMappingException { return(T)_readMapAndClose(_jsonFactory.createParser(content),_typeFactory.constructType(valueType)); }
//获取json解析器,其中包含带解析的串
publicJsonParsercreateParser(Stringcontent)throwsIOException,JsonParseException{
finalintstrLen=content.length();
//Actually,let'susethisformedium-sizedcontent,upto64kBchunk(32kbchar)
if(_inputDecorator!=null||strLen>0x8000||!canUseCharArrays()){
//easiertojustwrapinaReaderthanextendInputDecorator;or,ifcontent
//istoolongforustocopyitover
returncreateParser(newStringReader(content));
}
IOContextctxt=_createContext(content,true);
char[]buf=ctxt.allocTokenBuffer(strLen);
content.getChars(0,strLen,buf,0);
return_createParser(buf,0,strLen,ctxt,true);
}
//将待解析的类型转化为JavaType类型
publicJavaTypeconstructType(Typetype){
return_constructType(type,null);
}
protectedJavaType_constructType(Typetype,TypeBindingscontext)
{
JavaTyperesultType;
//simpleclass?
if(typeinstanceofClass>){
resultType=_fromClass((Class>)type,context);
}
//Butifnot,needtostartresolving.
elseif(typeinstanceofParameterizedType){
resultType=_fromParamType((ParameterizedType)type,context);
}
elseif(typeinstanceofJavaType){//[Issue#116]
return(JavaType)type;
}
elseif(typeinstanceofGenericArrayType){
resultType=_fromArrayType((GenericArrayType)type,context);
}
elseif(typeinstanceofTypeVariable>){
resultType=_fromVariable((TypeVariable>)type,context);
}
elseif(typeinstanceofWildcardType){
resultType=_fromWildcard((WildcardType)type,context);
}else{
//sanitycheck
thrownewIllegalArgumentException("UnrecognizedType:"+((type==null)?"[null]":type.toString()));
}
if(_modifiers!=null&&!resultType.isContainerType()){
for(TypeModifiermod:_modifiers){
resultType=mod.modifyType(resultType,type,context,this);
}
}
returnresultType;
}
b、获取反序列化配置对象和上下文对象,进行第一步的序列化操作。
protectedObject_readMapAndClose(JsonParserjp,JavaTypevalueType)
throwsIOException,JsonParseException,JsonMappingException
{
try{
Objectresult;
DeserializationConfigcfg=getDeserializationConfig();
DeserializationContextctxt=createDeserializationContext(jp,cfg);
//依据valueType得到反序列化的解析器
//对象对应的是beanDeserializermap对应的是MapDeserializer。。。。
JsonDeserializer
c、跳入到BeanDeserializer类中。
下面以BeanDeserializer为例进行讲解:
@Override
publicObjectdeserialize(JsonParserp,DeserializationContextctxt)
throwsIOException
{
JsonTokent=p.getCurrentToken();
//commoncasefirst
if(t==JsonToken.START_OBJECT){//TODO:in2.6,use'p.hasTokenId()'
if(_vanillaProcessing){
returnvanillaDeserialize(p,ctxt,p.nextToken());
}
p.nextToken();
if(_objectIdReader!=null){
returndeserializeWithObjectId(p,ctxt);
}
returndeserializeFromObject(p,ctxt);
}
return_deserializeOther(p,ctxt,t);
}
/**
*Streamlinedversionthatisonlyusedwhenno"special"
*featuresareenabled.
*/
privatefinalObjectvanillaDeserialize(JsonParserp,
DeserializationContextctxt,JsonTokent)
throwsIOException
{
finalObjectbean=_valueInstantiator.createUsingDefault(ctxt);
//[databind#631]:Assigncurrentvalue,tobeaccessiblebycustomserializers
p.setCurrentValue(bean);
for(;t==JsonToken.FIELD_NAME;t=p.nextToken()){
StringpropName=p.getCurrentName();
p.nextToken();
if(!_beanProperties.findDeserializeAndSet(p,ctxt,bean,propName)){
handleUnknownVanilla(p,ctxt,bean,propName);
}
}
returnbean;
}
/**
*Conveniencemethodthattriestofindpropertywithgivenname,and
*ifitisfound,call{@linkSettableBeanProperty#deserializeAndSet}
*onit,andreturntrue;or,ifnotfound,returnfalse.
*Note,too,thatifdeserializationisattempted,possibleexceptions
*arewrappedifandasnecessary,socallerneednothandlethose.
*
*@since2.5
*/
publicbooleanfindDeserializeAndSet(JsonParserp,DeserializationContextctxt,
Objectbean,Stringkey)throwsIOException
{
if(_caseInsensitive){
key=key.toLowerCase();
}
intindex=key.hashCode()&_hashMask;
Bucketbucket=_buckets[index];
//Let'sunrollfirstlookupsincethatisnullormatchin90+%cases
if(bucket==null){
returnfalse;
}
//Primarilywedojustidentitycomparisonaskeysshouldbeinterned
if(bucket.key==key){
try{
bucket.value.deserializeAndSet(p,ctxt,bean);
}catch(Exceptione){
wrapAndThrow(e,bean,key,ctxt);
}
returntrue;
}
return_findDeserializeAndSet2(p,ctxt,bean,key,index);
}
MethodProperty
@Override
publicvoiddeserializeAndSet(JsonParserjp,DeserializationContextctxt,
Objectinstance)throwsIOException
{
Objectvalue=deserialize(jp,ctxt);
try{
//将得到的结果放入反序列化对应的对象中
_setter.invoke(instance,value);
}catch(Exceptione){
_throwAsIOE(e,value);
}
}
publicfinalObjectdeserialize(JsonParserp,DeserializationContextctxt)throwsIOException
{
JsonTokent=p.getCurrentToken();
if(t==JsonToken.VALUE_NULL){
return(_nullProvider==null)?null:_nullProvider.nullValue(ctxt);
}
if(_valueTypeDeserializer!=null){
return_valueDeserializer.deserializeWithType(p,ctxt,_valueTypeDeserializer);
}
return_valueDeserializer.deserialize(p,ctxt);
}
//如果继承了JsonDeserializer类重写了deseriakize方法,则会跳转到对应注入的类中进行处理
//不出意外的话最后都会调用DeserializationContext的readValue(JsonParserp,Classtype)方法,然后会根据type的类型跳转到对应的反序列化类中进行处理。
publicTreadValue(JsonParserp,Class type)throwsIOException{ returnreadValue(p,getTypeFactory().constructType(type)); } @SuppressWarnings("unchecked") public TreadValue(JsonParserp,JavaTypetype)throwsIOException{ //得到最终解析的类型,Mapliststring。。。。 JsonDeserializer
在不同的业务场景下,第三方接口返回的数据类型可能会发生变化,比如最初第三方业务代码是使用php实现的,而与之对接的服务器端也是用php实现的。后来,又成立了以Java为开发语言的服务器端开发小组,此时,对接第三方可能会出现问题。第三方返回数据类型的不唯一性,可能会使Java开发人员无法“正常”反序列化第三方接口返回的json串。例如:第三方接口返回的字段中,当字段为空时,返回的是数组;而字段不为空时,返回的却是对象。这样,那么通过ObjectMapper进行解析时,就会抛出异常,导致服务器端无法正常将数据返回给客户端。面对这样的问题,可能有以下两种解决方法:
第一种解决方法是对bean中每个字段set方法内进行判断,当解析字符串是一个数组时,则返回空对象;
当解析的字符串不为空时,就会特别的麻烦,默认情况下,会将Json串解析成一个map,其中key为bean中字段的名称,value为bean的值。这样,就需要创建一个新的bean,随后依次从map中取出对应字段的值,然后再set到bean中。显然,这种方式很麻烦,一旦第三方字段发生变化时,需要不停地维护这段代码。
第二种解决方法是继承JsonDeserialize,并重写反序列化方法。通过源码可知,JsonDeserializer抽象类是处理反序列化的类,只需在Bean类中的字段上加入注解@JsonDeserialize(using=xxx.class),并且xxx类要继承JsonDeserializer类,且重新对应的deserialize方法,在该方法中进行相应处理即可。在该方法中处理待反序列化字段可能出现的多种不同情况,详情见源码。
这里需要注意的是:当反序列化字段是一个对象,而第三方返回的数据为一个数组时,在重写deserialize方法时,如果判断出当前token指向的是一个数组,而此时需得到空对象。此时,不能直接返回空对象,必须调用readValue方法,目的是将token移动到正确的位置,否则,将创建一些奇怪的对象。
对于第二种解决方法,下面举例说明:
packagecom.string;
importjava.util.Map;
importcom.fasterxml.jackson.databind.annotation.JsonDeserialize;
publicclassComment{
publicStringid;
@JsonDeserialize(using=ImgPackSerializer.class)
publicMapimgPack;
@JsonDeserialize(using=CoopSerializer.class)
publicCoopcoop;
publicCoopgetCoop(){
returncoop;
}
publicvoidsetCoop(Coopcoop){
this.coop=coop;
}
publicMapgetImgPack(){
returnimgPack;
}
publicvoidsetImgPack(MapimgPack){
this.imgPack=imgPack;
}
publicStringgetId(){
returnid;
}
publicvoidsetId(Stringid){
this.id=id;
}
}
classCoop{
publicIntegerage;
publicIntegergetAge(){
returnage;
}
publicvoidsetAge(Integerage){
this.age=age;
}
}
packagecom.string;
importjava.io.IOException;
importjava.util.List;
importjava.util.Map;
importcom.fasterxml.jackson.core.JsonParser;
importcom.fasterxml.jackson.core.JsonProcessingException;
importcom.fasterxml.jackson.core.JsonToken;
importcom.fasterxml.jackson.databind.DeserializationContext;
importcom.fasterxml.jackson.databind.JsonDeserializer;
importcom.fasterxml.jackson.databind.ObjectMapper;
publicclassTestJson{
staticObjectMapperOBJECT_MAPPER=newObjectMapper();
publicstaticvoidmain(String[]args){
Strings="{\"code\":\"1\",\"comm\":[{\"imgPack\":{\"abc\":\"abc\"},\"coop\":[]}],\"name\":\"car\"}";
try{
ResponsereadValue=OBJECT_MAPPER.readValue(s,Response.class);
System.err.println(readValue.toString());
}catch(IOExceptione){
e.printStackTrace();
}
}
}
classResponse{
publicStringcode;
publicListcomm;
publicStringname;
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicStringgetCode(){
returncode;
}
publicvoidsetCode(Stringcode){
this.code=code;
}
publicListgetComm(){
returncomm;
}
publicvoidsetComm(Listcomm){
this.comm=comm;
}
}
classCoopSerializerextendsJsonDeserializer{
@Override
publicCoopdeserialize(JsonParserjp,DeserializationContextctxt)
throwsIOException,JsonProcessingException{
JsonTokencurrentToken=jp.getCurrentToken();
if(currentToken==JsonToken.START_ARRAY){
//returnnull;//errormaycreatemoreobject
//jp.nextToken();//error
returnctxt.readValue(jp,Object.class)==null?null:null;
}elseif(currentToken==JsonToken.START_OBJECT){
return(Coop)ctxt.readValue(jp,Coop.class);
}
returnnull;
}
}
classImgPackSerializerextendsJsonDeserializer
总结
以上就是本文关于实例解析Json反序列化之ObjectMapper(自定义实现反序列化方法)的全部内容,希望对大家有所帮助。欢迎大家参阅本站其他专题,有什么问题可以留言,小编会及时回复大家的。