AndroidでのGsonクラスライブラリの使用

17618 ワード

1.逆シーケンス化
1.public <T> T fromJson(String json, Class<T> classOfT)
2.public <T> T fromJson(String json, Type typeOfT)
3.public <T> T fromJson(Reader json, Class<T> classOfT)
4.public <T> T fromJson(Reader json, Type typeOfT)
5.public <T> T fromJson(JsonReader reader, Type typeOfT)
6.public <T> T fromJson(JsonElement json, Class<T> classOfT)
7.public <T> T fromJson(JsonElement json, Type typeOfT)
は基本的に使用されている以上のいくつかの方法をタイムリーに使用していますが、一般的には前の2つの方法を使用しています.
第1の方法では、基本的なオブジェクトタイプのみを解析できます.パッケージタイプとカスタムエンティティタイプです.
Student student = gson.fromJson(s1, Student.class);
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson(""abc"", String.class);
String anotherStr = gson.fromJson("["abc"]", String.class);
次に、具体的な解析プロセスを見てみましょう.
1.ここでClassタイプをTypeタイプに強く変換し、fromJsonメソッドを呼び出します.
 public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
    Object object = fromJson(json, (Type) classOfT);
    return Primitives.wrap(classOfT).cast(object);
  }

2.ここでjson文字列をStringReaderにカプセル化し、fromJsonメソッドを呼び出します.
  public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
    if (json == null) {
      return null;
    }
    StringReader reader = new StringReader(json);
    T target = (T) fromJson(reader, typeOfT);
    return target;
  }
3.ここでStringReaderをJsonReaderにカプセル化し、fromJsonメソッドを呼び出します.
 public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    JsonReader jsonReader = new JsonReader(json);
    T object = (T) fromJson(jsonReader, typeOfT);
    assertFullConsumption(object, jsonReader);
    return object;
  }
4.最後に呼び出されたメソッドは次のとおりです.
 public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
    boolean isEmpty = true;
    boolean oldLenient = reader.isLenient();
    reader.setLenient(true);
    try {
      reader.peek();
      isEmpty = false;
      TypeAdapter<T> typeAdapter = (TypeAdapter<T>) getAdapter(TypeToken.get(typeOfT));
      return typeAdapter.read(reader);
    } catch (EOFException e) {
      /*
       * For compatibility with JSON 1.5 and earlier, we return null for empty
       * documents instead of throwing.
       */
      if (isEmpty) {
        return null;
      }
      throw new JsonSyntaxException(e);
    } catch (IllegalStateException e) {
      throw new JsonSyntaxException(e);
    } catch (IOException e) {
      // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
      throw new JsonSyntaxException(e);
    } finally {
      reader.setLenient(oldLenient);
    }
  }
このメソッドでgetAdapterメソッドによりtypeOfTタイプに対応するTypeAdapterオブジェクトを取得しますが、具体的にはどのようにして取得しますか?次に、
5.ここではまずキャッシュされたmapからTypeAdapterオブジェクトを取得し、そうでなければ次のfactoriesファクトリ集合を巡り、
 public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
    TypeAdapter<?> cached = typeTokenCache.get(type);
    if (cached != null) {
      return (TypeAdapter<T>) cached;
    }

    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
    // the key and value type parameters always agree
    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
    if (ongoingCall != null) {
      return ongoingCall;
    }

    FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
    threadCalls.put(type, call);
    try {
      for (TypeAdapterFactory factory : factories) {
        TypeAdapter<T> candidate = factory.create(this, type);
        if (candidate != null) {
          call.setDelegate(candidate);
          typeTokenCache.put(type, candidate);
          return candidate;
        }
      }
      throw new IllegalArgumentException("GSON cannot handle " + type);
    } finally {
      threadCalls.remove(type);
    }
  }
6.次に、gsonのコンストラクション関数で初期化されたfactoriesセットに、対応するオブジェクトタイプのType Adapterオブジェクトを作成するためのファクトリが追加されたかを見てみましょう.
 // built-in type adapters that cannot be overridden
    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
    factories.add(ObjectTypeAdapter.FACTORY);

    // user's type adapters
    factories.addAll(typeAdapterFactories);

    // type adapters for basic platform types
    factories.add(TypeAdapters.STRING_FACTORY);
    factories.add(TypeAdapters.INTEGER_FACTORY);
    factories.add(TypeAdapters.BOOLEAN_FACTORY);
    factories.add(TypeAdapters.BYTE_FACTORY);
    factories.add(TypeAdapters.SHORT_FACTORY);
    factories.add(TypeAdapters.newFactory(long.class, Long.class,
            longAdapter(longSerializationPolicy)));
    factories.add(TypeAdapters.newFactory(double.class, Double.class,
            doubleAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.newFactory(float.class, Float.class,
            floatAdapter(serializeSpecialFloatingPointValues)));
    factories.add(TypeAdapters.NUMBER_FACTORY);
    factories.add(TypeAdapters.CHARACTER_FACTORY);
    factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
    factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
    factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
    factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
    factories.add(TypeAdapters.URL_FACTORY);
    factories.add(TypeAdapters.URI_FACTORY);
    factories.add(TypeAdapters.UUID_FACTORY);
    factories.add(TypeAdapters.LOCALE_FACTORY);
    factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
    factories.add(TypeAdapters.BIT_SET_FACTORY);
    factories.add(DateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.CALENDAR_FACTORY);
    factories.add(TimeTypeAdapter.FACTORY);
    factories.add(SqlDateTypeAdapter.FACTORY);
    factories.add(TypeAdapters.TIMESTAMP_FACTORY);
    factories.add(ArrayTypeAdapter.FACTORY);
    factories.add(TypeAdapters.ENUM_FACTORY);
    factories.add(TypeAdapters.CLASS_FACTORY);

    // the excluder must precede all adapters that handle user-defined types
    factories.add(excluder);

    // type adapters for composite and user-defined types
    factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
    factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
    factories.add(new ReflectiveTypeAdapterFactory(
        constructorConstructor, fieldNamingPolicy, excluder));

    this.factories = Collections.unmodifiableList(factories);
例えば基本的なTypeAdapterオブジェクトの工場はTypeAdaptersクラスにあるが、ここではTypeAdaptersを例に挙げることができる.STRING_FACTORYの工場:
 public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
public static <TT> TypeAdapterFactory newFactory(
      final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
    return new TypeAdapterFactory() {
      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
      public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
      }
      @Override public String toString() {
        return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
      }
    };
  }

カスタムエンティティタイプの場合、TypeAdaptersはObjectTypeAdapterです.FACTORY:
public final class ObjectTypeAdapter extends TypeAdapter<Object> {
  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
      if (type.getRawType() == Object.class) {
        return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
      }
      return null;
    }
  };

  private final Gson gson;

  private ObjectTypeAdapter(Gson gson) {
    this.gson = gson;
  }

  @Override public Object read(JsonReader in) throws IOException {
    JsonToken token = in.peek();
    switch (token) {
    case BEGIN_ARRAY:
      List<Object> list = new ArrayList<Object>();
      in.beginArray();
      while (in.hasNext()) {
        list.add(read(in));
      }
      in.endArray();
      return list;

    case BEGIN_OBJECT:
      Map<String, Object> map = new StringMap<Object>();
      in.beginObject();
      while (in.hasNext()) {
        map.put(in.nextName(), read(in));
      }
      in.endObject();
      return map;

    case STRING:
      return in.nextString();

    case NUMBER:
      return in.nextDouble();

    case BOOLEAN:
      return in.nextBoolean();

    case NULL:
      in.nextNull();
      return null;

    }
    throw new IllegalStateException();
  }

  @SuppressWarnings("unchecked")
  @Override public void write(JsonWriter out, Object value) throws IOException {
    if (value == null) {
      out.nullValue();
      return;
    }

    TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
    if (typeAdapter instanceof ObjectTypeAdapter) {
      out.beginObject();
      out.endObject();
      return;
    }

    typeAdapter.write(out, value);
  }
}
ここでBEGINならARRAYまたはBEGIN_OBJECTタイプでは、サブセットまたはサブオブジェクトがある可能性があります.オブジェクトであれば、最終的にkey-valueでmapに保存され、セットであればコレクションに戻ります.次にSTRING,NUMBER(double,long,int),BOOLEAN(true or false),NULL(null)タイプも可能である.
これにより対応するファクトリが作成され、createメソッドの呼び出しがTypeAdapterオブジェクトの作成になります.ここでgetAdapterメソッドのforループに戻り、最後に上のreadメソッドを呼び出して逆シーケンス化します.
次にTypeAdapterの使用を見てみましょう.
サーバから要求されたデータなど、自分が定義したタイプの解読を達成するためにreadとwriteの方法を書き換えることができます.このデータはオブジェクトの2つのフィールドに変換する必要があります.このreadでは、公式の例のように操作できます.
public class PointAdapter extends TypeAdapter<Point> {
    public Point read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        String xy = reader.nextString();
        String[] parts = xy.split(",");
        int x = Integer.parseInt(parts[0]);
        int y = Integer.parseInt(parts[1]);
        return new Point(x, y);
    }

    public void write(JsonWriter writer, Point value) throws IOException {
        if (value == null) {
            writer.nullValue();
            return;
        }
        String xy = value.getX() + "," + value.getY();
        writer.value(xy);
    }
}
readとwriteでnullの状況を確認したら、次のように使用します.
GsonBuilder builder = new GsonBuilder(); 
builder.registerTypeAdapter(Point.class, new PointAdapter());
Gson gson = builder.create();
nullを手動でチェックしていない場合は、次の操作を行います.
builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());

次に、システムのデフォルトの実装を示します.
DateTypeAdapter.java
たとえば、エンティティクラスに定義されたjavaがあります.util.Dateパッケージタイプのdateタイプです.逆シーケンス化すると、DateType Adapterを使用して、dateタイプに変換するフィールドを操作します.
  @Override public Date read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
      in.nextNull();
      return null;
    }
    return deserializeToDate(in.nextString());
  }

  private synchronized Date deserializeToDate(String json) {
    try {
      return localFormat.parse(json);
    } catch (ParseException ignored) {
    }
    try {
      return enUsFormat.parse(json);
    } catch (ParseException ignored) {
    }
    try {
      return iso8601Format.parse(json);
    } catch (ParseException e) {
      throw new JsonSyntaxException(json, e);
    }
  }
しかし、このときサーバが空の文字を返すと、変換は異常を報告する.前の2つの変換はすべてキャプチャされ、何の処理も行わないため、最後にthrow new JsonSyntaxException(json,e)を使用する.例外を投げ出してプログラムをクラッシュさせる:
だから自分で判断して異常を捕まえたほうがいいです.
 GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        @Override
        public Date deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
                throws JsonParseException {
            try {
                return df.parse(json.getAsString());
            } catch (ParseException e) {
                return null;
            }
        }
    });
    Gson gson = gsonBuilder.create();
このように異常が発生すると捕捉nullに戻る.
TimeTypeAdapter.java
エンティティクラスにjavaが定義されている場合.sql.Time内のTimeのタイプでは、逆シーケンス化を行うと、TimeType Adapterを使用して、Timeタイプに変換するフィールドを操作します.
  private final DateFormat format = new SimpleDateFormat("hh:mm:ss a");

  @Override public synchronized Time read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
      in.nextNull();
      return null;
    }
    try {
      Date date = format.parse(in.nextString());
      return new Time(date.getTime());
    } catch (ParseException e) {
      throw new JsonSyntaxException(e);
    }
  }

  @Override public synchronized void write(JsonWriter out, Time value) throws IOException {
    out.value(value == null ? null : format.format(value));
  }

SqlDateTypeAdapter.java
エンティティクラスにjavaが定義されている場合.sql.DateのDateのタイプは、逆シーケンス化を行う場合、SqlDateTypeAdapterを使用してDateタイプに変換するフィールドを操作します.
 private final DateFormat format = new SimpleDateFormat("MMM d, yyyy");

  @Override
  public synchronized java.sql.Date read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
      in.nextNull();
      return null;
    }
    try {
      final long utilDate = format.parse(in.nextString()).getTime();
      return new java.sql.Date(utilDate);
    } catch (ParseException e) {
      throw new JsonSyntaxException(e);
    }
  }

  @Override
  public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException {
    out.value(value == null ? null : format.format(value));
  }

ArrayTypeAdapter.java
エンティティ内の集合または配列は、クラスでフィルタされます.
MapTypeAdapterFactory.JAvaのAdapter内部クラス
エンティティ内のmapオブジェクトは、クラスでフィルタされます.
TreeTypeAdapter.java
JsonSerializerとJsonDeserializer実装クラスを間接的に呼び出す方法.
JsonDeserializerインタフェースの使用:
デフォルトでは、次の3つのメソッドが設定されている場合にのみ、インタフェースが実装されます.
public GsonBuilder setDateFormat(String pattern)
public GsonBuilder setDateFormat(int style)
public GsonBuilder setDateFormat(int dateStyle, int timeStyle)
は、以下の方法で示すことができる.
 private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
      List<TypeAdapterFactory> factories) {
    DefaultDateTypeAdapter dateTypeAdapter;
    if (datePattern != null && !"".equals(datePattern.trim())) {
      dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
    } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
      dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
    } else {
      return;
    }

    factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter));
    factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter));
    factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter));
  }

適用シーン:
public class Foo {

    Date date;
    Date created_at;

    public Foo(Date date, Date created_at){
       this.date = date;
       this.created_at = created_at;
    }

    @Override
    public String toString() {
       return "Foo [date=" + date + ", created_at=" + created_at + "]";
    }

}
public class FooDeserializer implements JsonDeserializer<Foo> {

     public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        String a = json.getAsJsonObject().get("date").getAsString();
        String b = json.getAsJsonObject().get("created_at").getAsString();

        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat sdfDateWithTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

        Date date, created;
        try {
           date = sdfDate.parse(a);
           created = sdfDateWithTime.parse(b);
        } catch (ParseException e) {
           throw new RuntimeException(e);
        }

        return new Foo(date, created);
    }

}
同じDateタイプであっても、異なるフォーマットの変換が可能(TypeAdapterを用いても同様に実現可能)
コレクションまたはmapオブジェクトの場合は、次のように書く必要があります.
map:[[{"x":5,"y":6},"a"],[{"x":8,"y":8},"b"]]
Map<Point, String> retMap = gson.fromJson(s, new TypeToken<Map<Point, String>>() {}.getType());
list:
List<Person> ps = gson.fromJson(str, new TypeToken<List<Person>>(){}.getType());