実例解析Json反プログレッシブObjectMapper(カスタム実装反プログレッシブ方法)


サーバー側の開発者にとっては、第三者インターフェースを呼び出してデータを取得し、その「エージェント」をクライアントに変換して返すのは日常茶飯事です。    一般的には、サードパーティ・インターフェースから返ってくるデータの種類はJson形式であり、サーバ開発者は、json形式のデータを対象に変換して処理し、カプセル化してクライアントに返す必要がある。
     効率を特に考慮していない場合(検索、キャッシュなどの状況に対して、thriftとprotobufferを使用することが考えられます)、通常はjacksonのカバンのObject Mapper類を選択してJson列に逆順番に並べて対応する対象を獲得します。通常はreadValue(String content、Class<T>valueType)を選択して逆順序化します。
     ObjectMapperのreadValue方法は、Json串とターゲットタイプによって、それぞれJson ParseとJavaTypeを作成し、Deserial Config、Deserialzation Controtext、Json Deserializerなどを生成し、その中でJson Deserializalizerの種類を解決します。JsonParsには解析対象文字列およびその他の情報が格納されており、解析中にtokenによって現在のマッチングのタイプを判断する(例えば、遭遇したら{対象タイプの開始位置と判断する;「集合タイプの開始位置と判断する」に遭遇し、タイプが確定すると、それに対応する逆プログレッシブクラスに飛び込み、結果を得て、tokenは後ろに移動し、次の列を解析する。同様の再帰的な方法として解析することができ、tokenによって一つのオブジェクトと判断されると、BeanDeserializerに飛び込み解析を行い、その後、オブジェクトのすべてのフィールドを巡回し、フィールドが文字列であればStering Deserializerにジャンプして解析を行い、セグメントが配列であればCollection Deserializerにジャンプして解析を行う。完全な文字列を解析するまで。似たように見えるかもしれませんが、木の深さが遍歴しているので、分かりやすいです。
Object MapperのreadValue方法について、逆順序化する過程を簡単に紹介します。
a:Json串刺しと対象タイプでJson Partserと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、ビーンDeserializer類に飛び込みます。

     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開発者が第三者インターフェースを「正常」に逆順序化して返すことができないかもしれません。たとえば、サードパーティ・インターフェースが返されるフィールドでは、フィールドが空の場合は、行列が返されます。フィールドが空でない場合は、オブジェクトを返します。このようにObject Mapperで解析すると、異常が発生し、サーバ側が正常にデータをクライアントに返すことができなくなります。このような問題に直面して、次の2つの解決方法があるかもしれません。
     第一の解決方法は、beanの各フィールドsetメソッド内の判定であり、解析文字列が配列である場合は、空のオブジェクトを返します。
解析した文字列が空でないと特に面倒です。デフォルトではJson列をmapに解析します。ここでkeyはbeanのフィールドの名前で、valueはbeanの値です。このようにして、新しいbeanを作成して、順次mapから対応するフィールドの値を取り出してから、beanにセットします。このような方法は面倒くさいです。一旦第三者フィールドが変化したら、このコードを維持し続ける必要があります。
     第二の解決方法はJson Deserializeを継承し、逆順序化方法を書き直すことです。ソースを通じて、Json Deserializerの抽象的なクラスは、アンチプログレッシブのクラスを処理するために、Beanクラスのフィールドに注解@Json Deserialize(using=xx.class)を追加するだけでなく、xxxクラスはJson Deserializerクラスを継承し、再対応する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(カスタム反プログレッシブ化方法を実現する)のすべての内容についてです。本サイトの他のテーマを参照してください。何か問題があったら、メッセージを残してください。