Android
Gson
サーチ…
前書き
Gsonは、JavaオブジェクトをJSON表現に変換するために使用できるJavaライブラリです。 Gsonは、これらの両方を非常に重要な設計目標と考えています。
Gsonの特徴:
単純なtoJson()
メソッドとfromJson()
メソッドを提供して、JavaオブジェクトをJSONに変換します。逆も同様です。
既存の変更不可能なオブジェクトをJSONとの間で変換できるようにする
Java Genericsの広範なサポート
任意の複雑なオブジェクトをサポートします(深い継承階層と汎用タイプの広範な使用)
構文
- Excluder excluder()
- FieldNamingStrategy fieldNamingStrategy()
- <T> fromJson(JsonElement json、Class <T> classOfT)
- <T> T fromJson(JsonElement json、Type typeOfT)
- <T> T fromJson(JsonReaderリーダー、Type typeOfT)
- <T> T fromJson(Reader json、Class <T> classOfT)
- <T> T fromJson(Reader json、Type typeOfT)
- <T> T fromJson(String json、Class <T> classOfT)
- <T> T fromJson(String json、Type typeOfT)
- <T> TypeAdapter <T> getAdapter(Class <T>型)
- <T> TypeAdapter <T> getAdapter(TypeToken <T>型)
- <T> TypeAdapter <T> getDelegateAdapter(TypeAdapterFactory skipPast、TypeToken <T>型)
- JsonReader newJsonReader(リーダーリーダー)
- JsonWriter newJsonWriter(ライターライター)
- JsonElement toJsonTree(オブジェクトsrc)
- JsonElement toJsonTree(Object src、Type typeOfSrc)
- ブール値serializeNulls()
- boolean htmlSafe()
- String toJson(JsonElement jsonElement)
- String toJson(Object src)
- String toJson(Object src、Type typeOfSrc)
- String toString()
- void toJson(Object src、Type typeOfSrc、追加可能なライター)
- void toJson(Object src、Type typeOfSrc、JsonWriterライター)
- void toJson(JsonElement jsonElement、追加可能なライター)
- void toJson(JsonElement jsonElement、JsonWriterライター)
- void toJson(Object src、追加可能なライター)
JSONをGsonで解析する
この例では、GoogleのGsonライブラリを使用してJSONオブジェクトを解析しています。
オブジェクトの解析:
class Robot {
//OPTIONAL - this annotation allows for the key to be different from the field name, and can be omitted if key and field name are same . Also this is good coding practice as it decouple your variable names with server keys name
@SerializedName("version")
private String version;
@SerializedName("age")
private int age;
@SerializedName("robotName")
private String name;
// optional : Benefit it allows to set default values and retain them, even if key is missing from Json response. Not required for primitive data types.
public Robot{
version = "";
name = "";
}
}
次に、解析を行う必要がある場合は、次のようにします。
String robotJson = "{
\"version\": \"JellyBean\",
\"age\": 3,
\"robotName\": \"Droid\"
}";
Gson gson = new Gson();
Robot robot = gson.fromJson(robotJson, Robot.class);
リストの解析:
JSONオブジェクトのリストを取得するときに、それらを解析してJavaオブジェクトに変換したいことがよくあります。
変換しようとするJSON文字列は次のとおりです。
{
"owned_dogs": [
{
"name": "Ron",
"age": 12,
"breed": "terrier"
},
{
"name": "Bob",
"age": 4,
"breed": "bulldog"
},
{
"name": "Johny",
"age": 3,
"breed": "golden retriever"
}
]
}
この特定のJSON配列には3つのオブジェクトが含まれています。 Javaコードでは、これらのオブジェクトをDog
オブジェクトにマップする必要があります。 Dogオブジェクトは次のようになります。
private class Dog {
public String name;
public int age;
@SerializedName("breed")
public String breedName;
}
JSON配列をDog[]
変換するには:
Dog[] arrayOfDogs = gson.fromJson(jsonArrayString, Dog[].class);
Dog[]
をJSON文字列に変換する:
String jsonArray = gson.toJson(arrayOfDogs, Dog[].class);
JSON配列をArrayList<Dog>
に変換するには、次のようにします。
Type typeListOfDogs = new TypeToken<List<Dog>>(){}.getType();
List<Dog> listOfDogs = gson.fromJson(jsonArrayString, typeListOfDogs);
TypeオブジェクトtypeListOfDogs
は、 Dog
オブジェクトのリストがどのように見えるかを定義します。 GSONはこの型オブジェクトを使用して、JSON配列を正しい値にマップできます。
また、 List<Dog>
をJSON配列に変換する方法も同様です。
String jsonArray = gson.toJson(listOfDogs, typeListOfDogs);
GsonでJSONプロパティをenumに解析する
Gsonで列挙するStringを解析する場合は、次のようにします。
{"status": "open"}
public enum Status {
@SerializedName("open")
OPEN,
@SerializedName("waiting")
WAITING,
@SerializedName("confirm")
CONFIRM,
@SerializedName("ready")
READY
}
リストの解析 Gsonと
方法1
Gson gson = new Gson();
String json = "[ \"Adam\", \"John\", \"Mary\" ]";
Type type = new TypeToken<List<String>>(){}.getType();
List<String> members = gson.fromJson(json, type);
Log.v("Members", members.toString());
これは、パラメータ化された型のクラスを取得できないため(つまり、 List<String>.class
呼び出すことができないため)、最も汎用的なコンテナクラスに便利です。
方法2
public class StringList extends ArrayList<String> { }
...
List<String> members = gson.fromJson(json, StringList.class);
あるいは、必要な型を常にサブクラス化し、そのクラスを渡すこともできます。しかし、 StringList
型のオブジェクトを返すので、これは常にベストプラクティスではありません。
AutoValueとGsonを使用したJSONシリアライゼーション/デシリアライゼーション
あなたのgradleルートファイルでのインポート
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
あなたのgradleアプリケーションファイルでのインポート
apt 'com.google.auto.value:auto-value:1.2'
apt 'com.ryanharter.auto.value:auto-value-gson:0.3.1'
provided 'com.jakewharton.auto.value:auto-value-annotations:1.2-update1'
provided 'org.glassfish:javax.annotation:10.0-b28'
自動作成でオブジェクトを作成する:
@AutoValue public abstract class SignIn {
@SerializedName("signin_token") public abstract String signinToken();
public abstract String username();
public static TypeAdapter<SignIn> typeAdapter(Gson gson) {
return new AutoValue_SignIn.GsonTypeAdapter(gson);
}
public static SignIn create(String signin, String username) {
return new AutoValue_SignIn(signin, username);
}
}
GsonBuilderでGsonコンバータを作成する
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(
new AutoValueGsonTypeAdapterFactory())
.create());
デシリアライズする
String myJsonData = "{
\"signin_token\": \"mySigninToken\",
\"username\": \"myUsername\" }";
SignIn signInData = gson.fromJson(myJsonData, Signin.class);
シリアライズ
Signin myData = SignIn.create("myTokenData", "myUsername");
String myJsonData = gson.toJson(myData);
Gsonを使用すると、POJOオブジェクトを使用してシリアル化および逆シリアル化コードを単純化することができます。副作用は、リフレクションがコストパフォーマンスに優れていることです。そのため、AutoValue-Gsonを使用してCustomTypeAdapterを生成すると、リフレクションコストが回避され、APIの変更が発生したときに更新するのは非常に簡単です。
Gsonを使ったJSONの汎用クラスオブジェクトへの解析
JSON文字列があるとします。
["first","second","third"]
このJSON文字列を解析してString
配列にすることができます:
Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
しかし、それをList<String>
オブジェクトに解析する場合は、 TypeToken
を使用する必要があります。
ここにサンプルがあります:
Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());
以下の2つのクラスがあるとします。
public class Outer<T> {
public int index;
public T data;
}
public class Person {
public String firstName;
public String lastName;
}
JSON文字列は、 Outer<Person>
というOuter<Person>
オブジェクトに解析する必要があります。
次の例は、このJSON文字列を関連する汎用クラスオブジェクトに解析する方法を示しています。
String json = "......";
Type userType = new TypeToken<Outer<Person>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
JSON文字列をOuter<List<Person>>
オブジェクトに解析する必要がある場合:
Type userListType = new TypeToken<Outer<List<Person>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType);
あなたのプロジェクトにGsonを追加する
dependencies {
compile 'com.google.code.gson:gson:2.8.1'
}
Gsonの最新バージョンを使用するには
以下の行はコンパイルするたびにgsonライブラリの最新バージョンをコンパイルしますが、バージョンを変更する必要はありません。
長所:最新の機能、スピード、バグの少ない機能を使用できます。
短所:コードとの互換性が損なわれる可能性があります。
compile 'com.google.code.gson:gson:+'
Gsonを使用してディスクからJSONファイルをロードする
これにより、ディスクからJSONファイルがロードされ、指定されたタイプに変換されます。
public static <T> T getFile(String fileName, Class<T> type) throws FileNotFoundException {
Gson gson = new GsonBuilder()
.create();
FileReader json = new FileReader(fileName);
return gson.fromJson(json, type);
}
Gsonにカスタムコンバータを追加する
場合によっては、いくつかのフィールドを希望のフォーマットで直列化または非直列化する必要があります。たとえば、バックエンドで日付に "YYYY-MM-dd HH:mm"という形式を使用し、POJOSでJoda TimeでDateTimeクラスを使用することができます。
これらの文字列をDateTimesオブジェクトに自動的に変換するには、カスタムコンバータを使用できます。
/**
* Gson serialiser/deserialiser for converting Joda {@link DateTime} objects.
*/
public class DateTimeConverter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> {
private final DateTimeFormatter dateTimeFormatter;
@Inject
public DateTimeConverter() {
this.dateTimeFormatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm");
}
@Override
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(dateTimeFormatter.print(src));
}
@Override
public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
if (json.getAsString() == null || json.getAsString().isEmpty()) {
return null;
}
return dateTimeFormatter.parseDateTime(json.getAsString());
}
}
Gsonに新しく作成されたコンバータを使用させるには、Gsonオブジェクトの作成時にそれを割り当てる必要があります:
DateTimeConverter dateTimeConverter = new DateTimeConverter();
Gson gson = new GsonBuilder().registerTypeAdapter(DateTime.class, dateTimeConverter)
.create();
String s = gson.toJson(DateTime.now());
// this will show the date in the desired format
その形式で日付を逆シリアル化するには、DateTime形式のフィールドを定義するだけです。
public class SomePojo {
private DateTime someDate;
}
GsonはDateTime型のフィールドを検出すると、フィールドを逆シリアル化するためにコンバーターを呼び出します。
RetrofitでシリアライザとしてGsonを使用する
まず、build.gradleファイルにGsonConverterFactory
を追加する必要があります
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
次に、Retrofit Serviceを作成するときにコンバータファクトリを追加する必要があります。
Gson gson = new GsonBuilder().create();
new Retrofit.Builder()
.baseUrl(someUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(RetrofitService.class);
ファクトリに渡すGsonオブジェクトを作成するときにカスタムコンバータを追加することができます。カスタム型変換を作成できるようにします。
Gsonを使用してジェネリッククラスにjson配列を解析する
jsonがあるとします。
{
"total_count": 132,
"page_size": 2,
"page_index": 1,
"twitter_posts": [
{
"created_on": 1465935152,
"tweet_id": 210462857140252672,
"tweet": "Along with our new #Twitterbird, we've also updated our Display Guidelines",
"url": "https://twitter.com/twitterapi/status/210462857140252672"
},
{
"created_on": 1465995741,
"tweet_id": 735128881808691200,
"tweet": "Information on the upcoming changes to Tweets is now on the developer site",
"url": "https://twitter.com/twitterapi/status/735128881808691200"
}
]
}
この配列をカスタムツイート(ツイートのリストコンテナ)オブジェクトに手動で解析することはできますが、 fromJson
メソッドをfromJson
する方が簡単です。
Gson gson = new Gson();
String jsonArray = "....";
Tweets tweets = gson.fromJson(jsonArray, Tweets.class);
以下の2つのクラスがあるとします。
class Tweets {
@SerializedName("total_count")
int totalCount;
@SerializedName("page_size")
int pageSize;
@SerializedName("page_index")
int pageIndex;
// all you need to do it is just define List variable with correct name
@SerializedName("twitter_posts")
List<Tweet> tweets;
}
class Tweet {
@SerializedName("created_on")
long createdOn;
@SerializedName("tweet_id")
String tweetId;
@SerializedName("tweet")
String tweetBody;
@SerializedName("url")
String url;
}
json配列を解析するだけであれば、このコードを解析することができます:
String tweetsJsonArray = "[{.....},{.....}]"
List<Tweet> tweets = gson.fromJson(tweetsJsonArray, new TypeToken<List<Tweet>>() {}.getType());
Gsonを使用したカスタムJSONデシリアライザ
たとえば、 /Date(1465935152)/
ように、すべての応答のすべての日付があるカスタム・フォーマットであるとし、すべてのJsonの日付をJavaのDate
インスタンスに逆シリアル化する必要があるとします。この場合は、カスタムJson Deserializerを実装する必要があります。
jsonの例:
{
"id": 1,
"created_on": "Date(1465935152)",
"updated_on": "Date(1465968945)",
"name": "Oleksandr"
}
以下のクラスがあるとします。
class User {
@SerializedName("id")
long id;
@SerializedName("created_on")
Date createdOn;
@SerializedName("updated_on")
Date updatedOn;
@SerializedName("name")
String name;
}
カスタムデシリアライザ:
class DateDeSerializer implements JsonDeserializer<Date> {
private static final String DATE_PREFIX = "/Date(";
private static final String DATE_SUFFIX = ")/";
@Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String dateString = json.getAsString();
if (dateString.startsWith(DATE_PREFIX) && dateString.endsWith(DATE_SUFFIX)) {
dateString = dateString.substring(DATE_PREFIX.length(), dateString.length() - DATE_SUFFIX.length());
} else {
throw new JsonParseException("Wrong date format: " + dateString);
}
return new Date(Long.parseLong(dateString) - TimeZone.getDefault().getRawOffset());
}
}
そして使用法:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateDeSerializer())
.create();
String json = "....";
User user = gson.fromJson(json, User.class);
JacksonのJSON文字列をDate型でシリアル化および逆シリアル化する
これは、たとえば、Gsonの日付変換をJacksonと互換性を持たせたい場合にも当てはまります。
Jacksonは、通常、Dateを "エポックからのミリ秒"にシリアル化しますが、GsonはAug 31, 2016 10:26:17
ような読み取り可能な形式を使用してDateを表します。ジャクソン形式の日付を逆シリアル化しようとすると、GsonのJsonSyntaxExceptionsが発生します。
これを回避するために、カスタムシリアライザとカスタムデシリアライザを追加できます。
JsonSerializer<Date> ser = new JsonSerializer<Date>() {
@Override
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext
context) {
return src == null ? null : new JsonPrimitive(src.getTime());
}
};
JsonDeserializer<Date> deser = new JsonDeserializer<Date>() {
@Override
public Date deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return json == null ? null : new Date(json.getAsLong());
}
};
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, ser)
.registerTypeAdapter(Date.class, deser)
.create();
Gsonを継承で使用する
Gsonはボックスからの継承をサポートしていません。
私たちは次のクラス階層を持っているとしましょう:
public class BaseClass {
int a;
public int getInt() {
return a;
}
}
public class DerivedClass1 extends BaseClass {
int b;
@Override
public int getInt() {
return b;
}
}
public class DerivedClass2 extends BaseClass {
int c;
@Override
public int getInt() {
return c;
}
}
そして、 DerivedClass1
インスタンスをJSON文字列にシリアル化したい
DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
Gson gson = new Gson();
String derivedClass1Json = gson.toJson(derivedClass1);
さて、別の場所では、このjson文字列を受け取り、それを逆シリアル化したいのですが、コンパイル時には、 BaseClass
インスタンスであることがBaseClass
ます。
BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
System.out.println(maybeDerivedClass1.getInt());
しかしGSONは知りませんderivedClass1Json
もともとのインスタンスだったDerivedClass1
、これは10をプリントアウトします。
これを解決するには?
そのようなケースを処理する独自のJsonDeserializer
を構築する必要があります。解決策は完全にきれいではありませんが、私はより良いものを考え出すことができませんでした。
まず、基本クラスに次のフィールドを追加します
@SerializedName("type")
private String typeName;
そしてそれを基本クラスのコンストラクタで初期化する
public BaseClass() {
typeName = getClass().getName();
}
次のクラスを追加します。
public class JsonDeserializerWithInheritance<T> implements JsonDeserializer<T> {
@Override
public T deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonPrimitive classNamePrimitive = (JsonPrimitive) jsonObject.get("type");
String className = classNamePrimitive.getAsString();
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new JsonParseException(e.getMessage());
}
return context.deserialize(jsonObject, clazz);
}
}
すべては、すべてをフックすることが残っている -
GsonBuilder builder = new GsonBuilder();
builder
.registerTypeAdapter(BaseClass.class, new JsonDeserializerWithInheritance<BaseClass>());
Gson gson = builder.create();
そして今、以下のコードを実行して、
DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
String derivedClass1Json = gson.toJson(derivedClass1);
BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
System.out.println(maybeDerivedClass1.getInt());
5が印刷されます。