Gsonを使用してJSONをマップに変換する
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で利用可能]です。