欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

实例解析Json反序列化之ObjectMapper(自定义实现反序列化方法)

程序员文章站 2024-03-02 13:09:04
     对于服务器端开发人员而言,调用第三方接口获取数据,将其“代理”转化并返给客户端几乎是家常便饭的事儿。  ...

     对于服务器端开发人员而言,调用第三方接口获取数据,将其“代理”转化并返给客户端几乎是家常便饭的事儿。    一般情况下,第三方接口返回的数据类型是json格式,而服务器开发人员则需将json格式的数据转换成对象,继而对其进行处理并封装,以返回给客户端。

     在不是特别考虑效率的情况下(对于搜索、缓存等情形可以考虑使用thrift和protobuffer),通常我们会选取jackson包中的objectmapper类对串反序列化以得到相应对象。通常会选取readvalue(string content, class<t>valuetype)方法进行反序列化。

     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。

  public <t> t readvalue(string content, class<t> valuetype)
    throws ioexception, jsonparseexception, jsonmappingexception
  {
    return (t) _readmapandclose(_jsonfactory.createparser(content), _typefactory.constructtype(valuetype));
  } 
  //获取json解析器,其中包含带解析的串
  public jsonparser createparser(string content) throws ioexception, jsonparseexception {
    final int strlen = content.length();
    // actually, let's use this for medium-sized content, up to 64kb chunk (32kb char)
    if (_inputdecorator != null || strlen > 0x8000 || !canusechararrays()) {
      // easier to just wrap in a reader than extend inputdecorator; or, if content
      // is too long for us to copy it over
      return createparser(new stringreader(content));
    }
    iocontext ctxt = _createcontext(content, true);
    char[] buf = ctxt.alloctokenbuffer(strlen);
    content.getchars(0, strlen, buf, 0);
    return _createparser(buf, 0, strlen, ctxt, true);
  }
  //将待解析的类型转化为javatype类型
  public javatype constructtype(type type) {
    return _constructtype(type, null);
  }
    protected javatype _constructtype(type type, typebindings context)
  {
    javatype resulttype;
    // simple class?
    if (type instanceof class<?>) {
      resulttype = _fromclass((class<?>) type, context);
    }
    // but if not, need to start resolving.
    else if (type instanceof parameterizedtype) {
      resulttype = _fromparamtype((parameterizedtype) type, context);
    }
    else if (type instanceof javatype) { // [issue#116]
      return (javatype) type;
    }
    else if (type instanceof genericarraytype) {
      resulttype = _fromarraytype((genericarraytype) type, context);
    }
    else if (type instanceof typevariable<?>) {
      resulttype = _fromvariable((typevariable<?>) type, context);
    }
    else if (type instanceof wildcardtype) {
      resulttype = _fromwildcard((wildcardtype) type, context);
    } else {
      // sanity check
      throw new illegalargumentexception("unrecognized type: "+((type == null) ? "[null]" : type.tostring()));
    }
    if (_modifiers != null && !resulttype.iscontainertype()) {
      for (typemodifier mod : _modifiers) {
        resulttype = mod.modifytype(resulttype, type, context, this);
      }
    }
    return resulttype;
  }

b、获取反序列化配置对象和上下文对象,进行第一步的序列化操作。

   protected object _readmapandclose(jsonparser jp, javatype valuetype)
    throws ioexception, jsonparseexception, jsonmappingexception
  {
    try {
      object result;
        deserializationconfig cfg = getdeserializationconfig();
        deserializationcontext ctxt = createdeserializationcontext(jp, cfg);
        //依据valuetype得到反序列化的解析器
        // 对象对应的是beandeserializer map对应的是mapdeserializer 。。。。
        jsondeserializer<object> deser = _findrootdeserializer(ctxt, valuetype);
        if (cfg.userootwrapping()) {
          result = _unwrapanddeserialize(jp, ctxt, cfg, valuetype, deser);
        } else {
        	//如果是对象,则调到beandeserializer类中进行解析
          result = deser.deserialize(jp, ctxt);
        }
        ctxt.checkunresolvedobjectid();
      }
      // need to consume the token too
      jp.clearcurrenttoken();
      return result;
    } finally {
      try {
        jp.close();
      } catch (ioexception ioe) { }
    }
  }

c、跳入到beandeserializer类中。

  下面以beandeserializer为例进行讲解:
    @override
  public object deserialize(jsonparser p, deserializationcontext ctxt)
    throws ioexception
  {
    jsontoken t = p.getcurrenttoken();
    // common case first
    if (t == jsontoken.start_object) { // todo: in 2.6, use 'p.hastokenid()'
      if (_vanillaprocessing) {
        return vanilladeserialize(p, ctxt, p.nexttoken());
      }
      p.nexttoken();
      if (_objectidreader != null) {
        return deserializewithobjectid(p, ctxt);
      }
      return deserializefromobject(p, ctxt);
    }
    return _deserializeother(p, ctxt, t);
  }
  /**
   * streamlined version that is only used when no "special"
   * features are enabled.
   */
  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 serializers
    p.setcurrentvalue(bean);
    for (; t == jsontoken.field_name; t = p.nexttoken()) {
      string propname = p.getcurrentname();
      p.nexttoken();
      if (!_beanproperties.finddeserializeandset(p, ctxt, bean, propname)) {
        handleunknownvanilla(p, ctxt, bean, propname);
      }
    }
    return bean;
  }
    /**
   * convenience method that tries to find property with given name, and
   * if it is found, call {@link settablebeanproperty#deserializeandset}
   * on it, and return true; or, if not found, return false.
   * note, too, that if deserialization is attempted, possible exceptions
   * are wrapped if and as necessary, so caller need not handle those.
   * 
   * @since 2.5
   */
  public boolean finddeserializeandset(jsonparser p, deserializationcontext ctxt,
      object bean, string key) throws ioexception
  {
    if (_caseinsensitive) {
      key = key.tolowercase();
    }
    int index = key.hashcode() & _hashmask;
    bucket bucket = _buckets[index];
    // let's unroll first lookup since that is null or match in 90+% cases
    if (bucket == null) {
      return false;
    }
    // primarily we do just identity comparison as keys should be interned
    if (bucket.key == key) {
      try {
        bucket.value.deserializeandset(p, ctxt, bean);
      } catch (exception e) {
        wrapandthrow(e, bean, key, ctxt);
      }
      return true;
    } 
    return _finddeserializeandset2(p, ctxt, bean, key, index);
  }
methodproperty
  @override
  public void deserializeandset(jsonparser jp, deserializationcontext ctxt,
      object instance) throws ioexception
  {
    object value = deserialize(jp, ctxt);
    try {
    	//将得到的结果放入反序列化对应的对象中
      _setter.invoke(instance, value);
    } catch (exception e) {
      _throwasioe(e, value);
    }
  }
 public final object deserialize(jsonparser p, deserializationcontext ctxt) throws ioexception
  {
    jsontoken t = 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(jsonparser p, class<t> type)方法,然后会根据type的类型跳转到对应的反序列化类中进行处理。
public <t> t readvalue(jsonparser p, class<t> type) throws ioexception {
    return readvalue(p, gettypefactory().constructtype(type));
  }
  @suppresswarnings("unchecked")
  public <t> t readvalue(jsonparser p, javatype type) throws ioexception {
  	//得到最终解析的类型,map list string。。。。
    jsondeserializer<object> deser = findrootvaluedeserializer(type);
    if (deser == null) {
    }
    return (t) deser.deserialize(p, this);
  }
  //例如这里如果是一个map,则会调用mapdeserializer的deserizlize方法得到最后的返回结果。
  //对于集合类,会通过token按照顺序解析生成一个个的集合对象并放入集合中。
jsontoken t;
    while ((t = p.nexttoken()) != jsontoken.end_array) {
      try {
        object value;
        if (t == jsontoken.value_null) {
          value = valuedes.getnullvalue();
        } else if (typedeser == null) {
          value = valuedes.deserialize(p, ctxt);
        } else {
          value = valuedes.deserializewithtype(p, ctxt, typedeser);
        }
        if (referringaccumulator != null) {
          referringaccumulator.add(value);
        } else {
          result.add(value);
        }
      } catch (unresolvedforwardreference reference) {
        if (referringaccumulator == null) {
          throw jsonmappingexception
              .from(p, "unresolved forward reference but no identity info", reference);
        }
        referring ref = referringaccumulator.handleunresolvedreference(reference);
        reference.getroid().appendreferring(ref);
      } catch (exception e) {
        throw jsonmappingexception.wrapwithpath(e, result, result.size());
      }
    }
    return result;

 在不同的业务场景下,第三方接口返回的数据类型可能会发生变化,比如最初第三方业务代码是使用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移动到正确的位置,否则,将创建一些奇怪的对象。

     对于第二种解决方法,下面举例说明:

package com.string;
import java.util.map;
import com.fasterxml.jackson.databind.annotation.jsondeserialize;
public class comment {
  public string id;
  @jsondeserialize(using = imgpackserializer.class)
  public map<string, string> imgpack;
  @jsondeserialize(using = coopserializer.class)
  public coop coop;
  public coop getcoop() {
    return coop;
  }
  public void setcoop(coop coop) {
    this.coop = coop;
  }
  public map<string, string> getimgpack() {
    return imgpack;
  }
  public void setimgpack(map<string, string> imgpack) {
    this.imgpack = imgpack;
  }
  public string getid() {
    return id;
  }
  public void setid(string id) {
    this.id = id;
  }
}
class coop {
  public integer age;
  public integer getage() {
    return age;
  }
  public void setage(integer age) {
    this.age = age;
  }
}
package com.string;
import java.io.ioexception;
import java.util.list;
import java.util.map;
import com.fasterxml.jackson.core.jsonparser;
import com.fasterxml.jackson.core.jsonprocessingexception;
import com.fasterxml.jackson.core.jsontoken;
import com.fasterxml.jackson.databind.deserializationcontext;
import com.fasterxml.jackson.databind.jsondeserializer;
import com.fasterxml.jackson.databind.objectmapper;
public class testjson {
  static objectmapper object_mapper = new objectmapper();

  public static void main(string[] args) {
    string s = "{\"code\":\"1\",\"comm\":[{\"imgpack\":{\"abc\":\"abc\"},\"coop\":[]}],\"name\":\"car\"}";
    try {
      response readvalue = object_mapper.readvalue(s, response.class);
      system.err.println(readvalue.tostring());
    } catch (ioexception e) {
      e.printstacktrace();
    }
  }
}
class response {
  public string code;
  public list<comment> comm;
  public string name;

  public string getname() {
    return name;
  }
  public void setname(string name) {
    this.name = name;
  }
  public string getcode() {
    return code;
  }
  public void setcode(string code) {
    this.code = code;
  }
  public list<comment> getcomm() {
    return comm;
  }
  public void setcomm(list<comment> comm) {
    this.comm = comm;
  }
}
class coopserializer extends jsondeserializer<coop> {
  @override
  public coop deserialize(jsonparser jp, deserializationcontext ctxt)
      throws ioexception, jsonprocessingexception {
    jsontoken currenttoken = jp.getcurrenttoken();
    if (currenttoken == jsontoken.start_array) {
      // return null; //error may create more object
      // jp.nexttoken(); //error
      return ctxt.readvalue(jp, object.class) == null ? null : null;
    } else if (currenttoken == jsontoken.start_object) {
      return (coop) ctxt.readvalue(jp, coop.class);
    }
    return null;
  }
}
class imgpackserializer extends jsondeserializer<map<string, string>> {
  @override
  public map<string, string> deserialize(jsonparser jp,
      deserializationcontext ctxt) throws ioexception,
      jsonprocessingexception {
    jsontoken currenttoken = jp.getcurrenttoken();
    if (currenttoken == jsontoken.start_array) {
      return ctxt.readvalue(jp, object.class) == null ? null : null;
    } else if (currenttoken == jsontoken.start_object) {
      return (map<string, string>) ctxt.readvalue(jp, map.class);
    }
    return null;
  }
}

总结

以上就是本文关于实例解析json反序列化之objectmapper(自定义实现反序列化方法)的全部内容,希望对大家有所帮助。欢迎大家参阅本站其他专题,有什么问题可以留言,小编会及时回复大家的。