Gsonを使用してJSONをマップに変換する

Gsonを使用してJSONをマップに変換する

1. 前書き

In this quick tutorial, we’ll learn how to convert a JSON string to a Map using Gson from Google

それを達成するための3つの異なるアプローチを見て、それらの長所と短所について、いくつかの実用的な例を挙げて説明します。

2. Map.classを渡す

一般に、Gson provides the following API in its Gson class to convert a JSON string to an object

public  T fromJson(String json, Class classOfT) throws JsonSyntaxException;

署名から、2番目のパラメーターがJSONで解析するオブジェクトのクラスであることは非常に明白です。 この場合、Map.classである必要があります。

String jsonString = "{'employee.name':'Bob','employee.salary':10000}";
Gson gson = new Gson();
Map map = gson.fromJson(jsonString, Map.class);
Assert.assertEquals(2, map.size());
Assert.assertEquals(Double.class, map.get("employee.salary").getClass());

このアプローチは、各プロパティの値の型に関して最良の推測を行います。

たとえば、数値はDoublesに、true falseBooleanに、オブジェクトはLinkedTreeMapsに強制変換されます。

ただし、重複するキーがある場合、強制は失敗し、JsonSyntaxException.がスローされます。

また、type erasure,が原因で、この強制動作を構成することもできません。 したがって、キーまたは値のタイプを指定する必要がある場合は、別のアプローチが必要になります。

3. TypeTokenの使用

To overcome the problem of type-erasure for the generic types, Gson has an overloaded version of the API

public  T fromJson(String json, Type typeOfT) throws JsonSyntaxException;

GsonのTypeTokenを使用して、タイプパラメータでMapを作成できます。 The TypeToken class returns an instance of ParameterizedTypeImpl that preserves the type of the key and value even at runtime

String jsonString = "{'Bob' : {'name': 'Bob Willis'},"
  + "'Jenny' : {'name': 'Jenny McCarthy'}, "
  + "'Steve' : {'name': 'Steven Waugh'}}";
Gson gson = new Gson();
Type empMapType = new TypeToken>() {}.getType();
Map nameEmployeeMap = gson.fromJson(jsonString, empMapType);
Assert.assertEquals(3, nameEmployeeMap.size());
Assert.assertEquals(Employee.class, nameEmployeeMap.get("Bob").getClass());

ここで、MapタイプをMap<String, Object>として作成すると、前のセクションで見たように、パーサーはデフォルトのままになります。

もちろん、これはまだプリミティブ型を強制するためにGsonにフォールバックします。 ただし、これらもカスタマイズできます。

4. カスタムJsonDeserializerの使用

Mapオブジェクトの構築をきめ細かく制御する必要がある場合は、タイプJsonDeserializer<Map>.のカスタムデシリアライザーを実装できます。

例を示すために、JSONに従業員の名前がキーとして含まれ、雇用日がその値として含まれていると仮定します。 さらに、日付の形式がyyyy/MM/ddであると仮定します。これは、Gsonの標準形式ではありません。

次に、JsonDeserializer:を実装することで、マップを別の方法で解析するようにGsonを構成できます。

public class StringDateMapDeserializer implements JsonDeserializer> {

    private SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");

    @Override
    public Map deserialize(JsonElement elem,
          Type type,
          JsonDeserializationContext jsonDeserializationContext) {
        return elem.getAsJsonObject()
          .entrySet()
          .stream()
          .filter(e -> e.getValue().isJsonPrimitive())
          .filter(e -> e.getValue().getAsJsonPrimitive().isString())
          .collect(
            Collectors.toMap(
              Map.Entry::getKey,
              e -> formatDate(e.getValue())));
    }

    private Date formatDate(Object value) {
        try {
            return format(value.getAsString());
        } catch (ParseException ex) {
            throw new JsonParseException(ex);
        }
    }
}

ここで、ターゲットタイプMap<String, Date>に対してGsonBuilderに登録し、カスタマイズされたGsonオブジェクトを作成する必要があります。

このGsonオブジェクトでfromJson APIを呼び出すと、パーサーはカスタムデシリアライザーを呼び出し、目的のMapインスタンスを返します。

String jsonString = "{'Bob': '2017-06-01', 'Jennie':'2015-01-03'}";
Type type = new TypeToken>(){}.getType();
Gson gson = new GsonBuilder()
  .registerTypeAdapter(type, new StringDateMapDeserializer())
  .create();
Map empJoiningDateMap = gson.fromJson(jsonString, type);
Assert.assertEquals(2, empJoiningDateMap.size());
Assert.assertEquals(Date.class, empJoiningDateMap.get("Bob").getClass());

この戦術は、マップに異種の値が含まれている可能性があり、そこにいくつの異なるタイプの値が存在する可能性があるかについて公正な考えを持っている場合にも役立ちます。

Gsonのカスタムデシリアライザーの詳細については、Gson Deserialization Cookbookを自由に確認してください。

5. 結論

この短い記事では、JSON形式の文字列からマップを作成するいくつかの方法を学びました。 また、これらのバリエーションの適切なユースケースについても説明しました。

例のソースコードは利用可能なover on GitHubです。