首页 > 开发 > Java > 正文

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

2024-07-13 10:11:17
字体:
来源:转载
供稿:网友

     对于服务器端开发人员而言,调用第三方接口获取数据,将其“代理”转化并返给客户端几乎是家常便饭的事儿。    一般情况下,第三方接口返回的数据类型是javascript/239484.html">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(自定义实现反序列化方法)的全部内容,希望对大家有所帮助。欢迎大家参阅本站其他专题,有什么问题可以留言,小编会及时回复大家的。


注:相关教程知识阅读请移步到JAVA教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表