1.はじめに

  • このクイックチュートリアルでは、Google ** のhttps://github.com/google/gson[

    Gson

    ]を使用してJSON文字列を

    Map

    に変換する方法を学習します。

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

2.

Map.class

を渡す

一般に、

Gsonはその

Gson

クラスに次のAPIを提供してJSON文字列をオブジェクト

に変換します。

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


sに、


true




false



Boolean

に、オブジェクトは


LinkedTreeMap

__sに強制変換されます。

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

そして、


type erasure

のせいで、

この強制的な振る舞いを設定することはできません。したがって、キーや値の種類を指定する必要がある場合は、別の方法が必要になります。


3

TypeToken


を使用する

  • ジェネリック型の型消去の問題を解決するために、

    Gson

    にはオーバーロードされたバージョンのAPI ** があります。

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

Gsonの

TypeToken

を使用して、typeパラメータを使用して

Map

を作成できます。 **

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());

ここで、

Map

型を

Map <String、Object>

として構築した場合、前のセクションで説明したようにパーサーはデフォルトのままになります。

もちろん、これはまだプリミティブ型を強制するためにGsonにフォールバックします。

ただし、それらもカスタマイズできます。


4カスタム

JsonDeserializer


を使う


  • Map

    オブジェクトの構築をきめ細かく制御する必要がある場合は、

    JsonDeserializer <Map> .

    ** 型のカスタムデシリアライザーを実装できます。

例を見るために、私たちの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);
        }
    }
}

それでは、ターゲット型

__Map <String、Date


>に対して

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

のカスタムデシリアライザの詳細については、https://www.baeldung.com/gson-deserialization-guide[Gson Deserialization Cookbook]を参照してください。


5結論

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

例のソースコードはhttps://github.com/eugenp/tutorials/tree/master/gson[GitHubで利用可能]です。