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

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

在不是特别考虑效率的情况下(对于搜索、缓存等情形可以考虑使用thrift和protobuffer),通常我们会选取jackson包中的ObjectMapper类对json串反序列化以得到相应对象。通常会选取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(自定义实现反序列化方法)的全部内容,希望对大家有所帮助。欢迎大家参阅本站其他专题,有什么问题可以留言,小编会及时回复大家的。

(0)

相关推荐

  • java对象序列化与反序列化的默认格式和json格式使用示例

    默认格式 复制代码 代码如下: public class MyClass implements Serializable{...} 序列化: 复制代码 代码如下: ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(outputPath)); output.writeObject(myObject); 反序列化: 复制代码 代码如下: ObjectInputStream input = new Objec

  • GSON实现Java对象的JSON序列化与反序列化的实例教程

    从GitHub下载GSON:https://github.com/google/gson Gson的应用主要为toJson与fromJson两个转换函数,而在使用这种对象转换之前需先创建好对象的类别以及其成员才能成功的将JSON字符串成功转换成相对应的对象. class Examples { private int answer1 = 100; private String answer2 = "Hello world!"; Examples(){ } // default const

  • java中fastjson生成和解析json数据(序列化和反序列化数据)

    本文讲解2点: 1. fastjson生成和解析json数据 (举例:4种常用类型:JavaBean,List<JavaBean>,List<String>,List<Map<String,Object>) 2.通过一个android程序测试fastjson的用法. fastjson简介: Fastjson是一个Java语言编写的高性能功能完善的JSON库.fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jack

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

    对于服务器端开发人员而言,调用第三方接口获取数据,将其"代理"转化并返给客户端几乎是家常便饭的事儿.    一般情况下,第三方接口返回的数据类型是json格式,而服务器开发人员则需将json格式的数据转换成对象,继而对其进行处理并封装,以返回给客户端. 在不是特别考虑效率的情况下(对于搜索.缓存等情形可以考虑使用thrift和protobuffer),通常我们会选取jackson包中的ObjectMapper类对json串反序列化以得到相应对象.通常会选取readValue(Strin

  • Python中解析JSON并同时进行自定义编码处理实例

    在对文件内容或字符串进行JSON反序列化(deserialize)时,由于原始内容编码问题,可能需要对反序列化后的内容进行编码处理(如将unicode对象转换为str). 在Python中,一种方式是先使用json.load或json.loads反序列化得到dict对象,然后对这个dict对象进行编码处理. 但其实在json.load与json.loads中,有可选参数object_hook.通过使用此参数,可以对反序列化得到的dict直接进行处理,并使用处理后新的dict替代原dict返回.

  • 通过实例解析json与jsonp原理及使用方法

    1.json与jsonp的引入 在ajax中 JSON用来解决数据交换问题,而JSONP来实现跨域. 备注:跨域也可以通过服务器端代理来解决; 理解:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议. 2.JSON:是一种基于文本的数据交换方式,或者叫做数据描述格式,是否该选用它首先肯定要关注它所拥有的优点. JSON的优点: 1) 基于纯文本,跨平台传递极其简单: 2) Javascript原生支持,后台语言几乎全部支持: 3) 轻量级数据

  • Zend Framework实现自定义过滤器的方法

    本文实例讲述了Zend Framework实现自定义过滤器的方法.分享给大家供大家参考,具体如下: 创建自定义的过滤器 代码: <?php require_once 'Zend/Filter/Interface.php'; class MyFilter implements Zend_Filter_Interface{ public function filter($value){ $badlist = array("梨","草莓","苹果"

  • Android编程实现自定义手势的方法详解

    本文实例讲述了Android编程实现自定义手势的方法.分享给大家供大家参考,具体如下: 之前介绍过如何在Android程序中使用手势,主要是系统默认提供的几个手势,这次介绍一下如何自定义手势,以及如何对其进行管理. 先介绍一下Android系统对手势的管理,Android系统允许应用程序把用户的手势以文件的形式保存以前,以后要使用这些手势只需要加载这个手势库文件即可,同时Android系统还提供了诸如手势识别.查找及删除等的函数接口,具体如下: 一.加载手势库文件: staticGestureL

  • C#编程实现自定义热键的方法

    本文实例讲述了C#编程实现自定义热键的方法.分享给大家供大家参考.具体实现方法如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing.Im

  • 微信小程序自定义toast实现方法详解【附demo源码下载】

    本文实例讲述了微信小程序自定义toast实现方法.分享给大家供大家参考,具体如下: 一.微信官方默认toast toast最常见了,几乎每个App都有这样的特效,先看下小程序自带的toast效果,立马想死的心都有了~~ 微信自带toast的效果: js文件: wx.showToast({ title: '成功', icon: 'success', duration: 2000 }) 用法超级简单,但官方小程序有几个问题: 只能显示success.loading两种icon 且icon不可去除 持

  • python使用json序列化datetime类型实例解析

    使用python的json模块序列化时间或者其他不支持的类型时会抛异常,例如下面的代码: # -*- coding: cp936 -*- from datetime import datetime import json if __name__=='__main__': now = datetime.now() json.dumps({'now':now}) 运行会出现下面的错误信息: Traceback (most recent call last): File "C:\Users\xx\De

  • 实例详解esp8266解析json数据的方法

    #include <ArduinoJson.h> void setup() { Serial.begin(115200); Serial.println("这里用于测试json数据的解析"); // DynamicJsonDocument对象 定义时候我们需要定义一个大小信息+复制大小 const size_t capacity = JSON_OBJECT_SIZE(2) + 30; DynamicJsonDocument doc(capacity); // 要解析的jso

  • 使用ObjectMapper解析json不用一直new了

    目录 前言 这代码有问题么? JMH测试结果 End 前言 自从国产之光fastjson频频暴雷,jackson json的使用是越来越广泛了.尤其是spring家族把它搞成了默认的JSON处理包,jackson的使用数量更是呈爆炸式发展. 很多同学发现,jackson并没有类似fastjson的JSON.parseObjec这样的,确实看起来很快的方法.要想解析json,你不得不new一个ObjectMapper,来处理真正的解析动作. 就像下面这样. public String getCar

随机推荐