1. 序章

このクイックチュートリアルでは、GoogleのGsonを使用してJSON文字列をマップに変換する方法を学習します。

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

2. Map.classを渡す

一般に、 Gsonは、JSON文字列をオブジェクトに変換するために、Gsonクラスで次のAPIを提供します。

public <T> T fromJson(String json, Class<T> 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());

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

たとえば、数値は Double に、truefalseブールに、オブジェクトはLinkedTreeMapに強制されます。 ]s。

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

また、型消去のため、この強制動作も構成できません。 したがって、キーまたは値の型を指定する必要がある場合は、別のアプローチが必要になります。

3. TypeTokenを使用する

ジェネリック型の型消去の問題を克服するために、GsonにはオーバーロードされたバージョンのAPIがあります

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

GsonのTypeTokenを使用して、タイプパラメータを使用してマップを作成できます。  TypeTokenクラスは、実行時でもキーと値のタイプを保持するParameterizedTypeImplのインスタンスを返します

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

さて、 地図次のように入力します地図 、その後、前のセクションで見たように、パーサーは引き続きデフォルトになります。

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

4. カスタムJsonDeserializerを使用する

建設をきめ細かく管理する必要がある場合地図オブジェクト、タイプのカスタムデシリアライザーを実装できます JsonDeserializer

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

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

public class StringDateMapDeserializer implements JsonDeserializer<Map<String, Date>> {

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

    @Override
    public Map<String, Date> 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);
        }
    }
}

今、私たちはそれをに登録する必要があります GsonBuilder ターゲットタイプに対して地図 >そしてカスタマイズされたビルド Gson 物体。

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

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

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

Gson のカスタムデシリアライザーの詳細については、 Gson DeserializationCookbookを参照してください。

5. 結論

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

例のソースコードは、GitHubから入手できます。