序文
この週末は公共の記事番号を持ついくつかの技術ブロガーれfastjsonの脆弱性は、このサービスが麻痺の害につながることが分かってきました!スクレーパーは、長くない脆弱性からのイベントの前に、fastjsonは深刻なバグが登場しました。現時点では、プロジェクトが使用fastjsonの多くは、JSONデータとオブジェクト、および更新されたバージョンを再配備する必要性を変換するために、時間がかかることと言うことができます。同時に、それは私の思考の新しい方法を提供し、強力なオープンソースのライブラリの多数の顔が、私たちは盲目的プロジェクトに導入することはできませんが、多くのオープンソースは、不安定要因は、プロジェクトが災害に見舞わ作るのに十分であるフレームワーク。また、役立つ小さなパートナーに期待して、優れたオープンソースのフレームワークGsonの同じJSONオブジェクトの変換と自宅で検討中で、週末を利用して、徐々にGson、研究対象のコンテンツの要約レコードの使用によって置き換えfastjson場所将来のプロジェクトで使用する予定です。
この論文に関わるすべてが、次のコードスニペットリポジトリにジュニアパートナー歓迎の参照を学ぶことに興味を持っています:
https://github.com/wrcj12138aaa/gson-actions
サポート対象:
- JDK 8
- Gson 2.8.5
- JUnitの5.5.1
- ロモ1.18.8
Gsonについて
正式導入Gsonに先立ち、我々はして開始することができます公式のwikiそれが何であるかを理解するために、Gsonの説明を見て?
Gsonは、そのJSON表現にJavaオブジェクトを変換するために使用することができるJavaライブラリです。また、同等のJavaオブジェクトにJSON文字列を変換するために使用することができます。
Javaオブジェクトとデータ列のJSON形式の変換の説明、Gson Javaライブラリから分かるように。最初はそれが広くGoogleの中のAndroidプラットフォームとJavaサーバ上で使用されています。2008年のオープンソースの後、オープンソースのフレームワークでは、となっている広く使用グーグルは、今までしている(2019年9月8日)は 1W6マルチ衛星されているだけでなく、春FrameworkクラスライブラリはGitHubのアリにジャクソンと同じ効果だけでなく、オープンソースの統合しましたfastjsonのように。
特性では、Gsonは、シンプルなAPIを提供fromJson/toJson
するJavaとJSONとの間の変換を達成するために、そしてコンパクトな、読みやすい出力JSON文字列、変換を生成することができ、複雑なオブジェクトは、カスタム日々発展を満たすのに十分豊か表しサポートしています私たちのJSONデータ処理ニーズのほとんど。
我々は、一般的に対象とコールJSON文字列のシリアライズとデシリアライズ(直列化/非直列化)との間の変換します。JSON文字列オブジェクトに変換は、JSON文字列にオブジェクトを直列化復元と呼ばれている変換するには、シリアライズと呼ばれています。
Gson基本的な使い方
使用Gsonフレームワーク配列とアンチ動作シーケンスは、から切り離せないcom.google.gson.Gson
物、それは共通のAPIシーケンスおよび逆シリアル化のモードが多数含まを提供し、また、重要なオブジェクトGsonフレームです。
Gsonは、オブジェクトを作成し、主に二つの方法があります。
- 使用して新しいキーワードを直接作成します:
Gson gson = new Gson()
- GsonBuilderオブジェクトによって構築されました:
Gson gson = new GsonBuilder().create()
典型的には、Gson挙動が同じである場合シリアライゼーションおよびデシリアライゼーションオペレーション中に2つの方法で、上記で作成されたオブジェクトが、このようなJSON形式の文字列として、追加のカスタム動作を可能にする、Gsonオブジェクトを構築する第二の方法出力コンテンツのシーケンスかどうかヌル値などが挙げられます。
Javaシリアル
単純なオブジェクトのシリアライズ
私たちは、上記の二つの異なる直列化されたJavaオブジェクトによって、以下の例の効果を見ることができます。
public class ResultTest {
@Test
void test_serialization() {
Gson gson = new Gson();
Result result = new Result(200, "成功", null);
String json = gson.toJson(result);
System.out.println("json is " + json);
Gson buildedGson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
String buildedJson = buildedGson.toJson(result);
System.out.println("buildedJson is " + buildedJson);
}
class Result {
private int code;
private String message;
private Object data;
public Result(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
}
}
コンソール以下のログ出力のテストケースを実行します。
この結果から、デフォルトのオブジェクトの振る舞いGsonシリアライズされたオブジェクトがしますnull
フィールドの値は無視され、com.google.gson.GsonBuilder#serializeNulls
アプローチがGsonオブジェクトのシリアル化が可能になるnull
フィールドを、JSON文字列とコンパクトな形式の通常のシーケンスは、メモリストリングを節約するために、使用com.google.gson.GsonBuilder#setPrettyPrinting
方法の最終出力がより読みやすい形式で後にJSON文字列を。もちろん、これらの2つの方法に加えて、GsonBuilder APIはまた、多くのカスタムシリアライズとデシリアライズの振る舞いを提供しています、我々はさらに背中の内容を説明します。
JSONを生成JosnObject
物体に加えて、JSONはまた、フレームワークJsonObject Gson一般的なオブジェクトを構築し、その後使用するために使用することができるよりも、カスタムクラスのように変換されるtoJson
JSON文字列を生成する方法を、テストクラスのクラスの下の元のテストに加え、以下の効果を確認するために実行
@Test
void test_jsonObject_serialization() {
Gson gson = new Gson();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("code", 400);
jsonObject.addProperty("message", "参数错误");
String toJson = gson.toJson(jsonObject);
String exceptedJson = "{\"code\":400,\"message\":\"参数错误\"}";
Assertions.assertEquals(exceptedJson, toJson); //true
}
JsonObject使用addProperty(property,value)
方法は内部のみが呼び出されるため、文字列、数値、ブール、文字データのこれらの四つのカテゴリーを追加するために使用することができcom.google.gson.JsonObject#add
、JsonPrimitiveオブジェクトにパッケージ化値を、その後、内部自己定義に保存されたLinkedTreeMap
変数メンバーのセットと、オブジェクトはJsonObjectする必要がある場合他のオブジェクトを追加するときに、あなたが直接する必要がadd(String property, JsonElement value)
JsonElementオブジェクトのメソッドを追加します。JsonElementはここJsonObjectとJsonPrimitiveはJsonElementを継承、抽象クラスであるので、我々は、元JsonObject上のオブジェクトの属性として別のJsonObjectオブジェクトで終わりました。
Gson gson = new Gson();
JsonObject jsonObject = new JsonObject();
//...
JsonObject nestJsonObject = new JsonObject();
nestJsonObject.addProperty("username", "one");
nestJsonObject.addProperty("score", 99);
jsonObject.add("data", nestJsonObject);
String toJson2 = gson.toJson(jsonObject);
System.out.println(toJson2);
// {"code":400,"message":"参数错误","data":{"username":"one","score":99}}
JSONデシリアライズ
単純なオブジェクトをデシリアライズ
今、私たちはJSONを見て主な使用はされての使用、Javaオブジェクトにデシリアライズcom.google.gson.Gson#fromJson
その最も基本的な使用はしているfromJson(String json, Class<T> classOfT)
変換が失敗した場合、指定されたClassオブジェクトにJSON文字列を試しに、スローされますJsonSyntaxException
例外を。私たちは、元のコードで実行facieの効果をテストケースを追加することができます。
@Test
void test_deserialization() {
String json = "{\"code\":400,\"message\":\"参数错误\"}";
Result result = new Gson().fromJson(json, Result.class);
Assertions.assertEquals(400, result.code); // true
Assertions.assertEquals("参数错误", result.message); // true
}
デシリアライズ地図
Javaのに加えて、カスタム外JSON文字列としてシリアライズされたオブジェクトでは、我々はまた、地図のコレクションを回すことができ、Gsonは地図の変換のセットを提供し、使用することも非常に簡単です:
@Test
void test_map() {
String jsonString = "{'employee.name':'one','employee.salary':10}";
Gson gson = new Gson();
Map map = gson.fromJson(jsonString, Map.class);
assertEquals(2, map.size());
assertEquals("one", map.get("employee.name"));
assertEquals(Double.class, map.get("employee.name").getClass());
}
変換マップのオブジェクトタイプの後に私たちは、多くの場合、HashMapを使うことは事実ではありませんが、Gsonのカスタムコレクションことに注意してくださいLinkedTreeMap
新しい上に実装され、キーと値のペアを格納するMapインタフェースを実装し、最適化、削除、および横断される最初で配置されるトラバースの順序として順に格納されたキーと値のペア。また、数値データのJSON文字列が順番にダブルタイプに変換され、そしてtrue/false
データをブール型である変換され、具体的な基準は、に基づいて決定することができるcom.google.gson.internal.bind.ObjectTypeAdapter#read
方法の実装。
JSONと配列、リストの変換
アレイJSON変換
我々は、通常のオブジェクトを変換するために、同様に、JSONデータ配列に変換される場合、toJson
直接JSONデータへの方法fromJson
の変換はアレイの対応する型に配列型を指定しました。
@Test
void test_array() {
Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};
String s = gson.toJson(ints);// [1,2,3,4,5]
assertEquals("[1,2,3,4,5]", s); // true
String s1 = gson.toJson(strings);// ["abc", "def", "ghi"]
assertEquals("[\"abc\",\"def\",\"ghi\"]", s1);
String[] strings1 = gson.fromJson(s1, String[].class);
assertEquals(strings.length, strings1.length); // true
assertEquals(strings[0], strings1[0]); // true
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class);
assertEquals(1, ints2[0]); // true
assertEquals(5, ints2[4]); // true
}
一覧JSON変換
配列データとして処理Gsonの方法を使用して、データにJSONデータを一覧表示する。ここでJSON若干異なる操作にリストオブジェクトデータについて主に、JSON配列データにカスタムクラスにリストは、我々は、元の文言に従うとき、次のように:
@Test
public void givenJsonString_whenIncorrectDeserializing() {
Gson gson = new Gson();
String inputString = "[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]";
List<Person> outputList = gson.fromJson(inputString, List.class);
outputList.get(0).getId();
}
しかし残念ながら、このコードを実行した後には、スローされますClassCastException
、次のように説明した例外を、:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.one.learn.Person
...
上記の説明から、我々は実行知ることができfromJson
、後のデシリアライズ後の代わりに、人の、リストの要素の型LinkedTreeMapを取得し、そのアクセスがスローされます際にPersonオブジェクトの道にid属性ClassCastException
異常。そして、それに対処する方法を、私たちは別のGson呼び出す必要がありますfromJson
:メソッドをfromJson(String json, Type typeOfT)
、使用の下に見えます
@Test
public void givenJsonString_whenCorrectDeserializing_() {
Gson gson = new Gson();
String inputString = "[{\"id\":1,\"name\":\"one\"},{\"id\":2,\"name\":\"two\"}]";
Type type = new TypeToken<List<Person>>(){}.getType();
List<Person> outputList = gson.fromJson(inputString, type);
int id = outputList.get(0).getId();
assertEquals(1, id); // true
assertEquals("one", outputList.get(0).getName()); // true
}
オブジェクトによるこの方法TypeToken TypeオブジェクトgetType
取得方法は、TypeTokenオブジェクトに関連する一般的なタイプです。そして、ここでTypeToken Gsonが原因TypeToken工法代表されるJavaのジェネリック型を提供することができない問題を解決するために、一般的なクラスをサポートするために導入されているprotected
、修正を直接構築することができない、我々は書かれて使用する必要がありますnew TypeToken<List<String>>() {}.getType()
フォームを。
Gson高度な使い方
Gsonの基本的な使用に接触した後、我々はさらにGsonの他の用途を研究するために行ってきました。
一般的なオブジェクトのデシリアライズ
ジェネリック医薬品のためのGsonサポートと単純な接触のセクションは、その後、最初にすべての私たちは、上記の結果クラス下の一般的なパラメータ調整を受け入れる、その強力な場所の下で実証するコードを使用します。
class Result<T> {
private int code;
private String message;
private T data;
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
}
次いで、JSON文字列埋め込みオブジェクトは、検索結果に解析される<ユーザー>オブジェクト、次のサンプルコード:
@Test
void test_genric_object() {
String json = "{\"code\":200,\"message\":\"操作成功\",\"data\":{\"username\": \"one\",\"avater\": \"image.jpg\"" +
"}}";
Type type = new TypeToken<Result<User>>(){}.getType();
Result<User> result = new Gson().fromJson(json, type);
Assertions.assertEquals(200, result.code);
Assertions.assertEquals("one", result.data.getUsername());
Assertions.assertEquals("image.jpg", result.data.getAvater());
}
class User {
private String username;
private String avater;
public String getUsername() {
return username;
}
public String getAvater() {
return avater;
}
}
ジェネリック型結果<ユーザー>を使用して詳細なTypeTokenオブジェクトの取得は、次にfromJson
入ってくるデシリアライズは、実行メソッドの対応するタイプに応じて動作します。
カスタムシリアライゼーション
Javaは、このような隠しフィールド、データフォーマットのフィールド、など我々はできる、シリアル化のロジックカスタム実装JsonSerializerインターフェイスの一部のシーケンスとして、特別な処理をオブジェクトの我々は、特定のフィールドをしたい場合。例えば、我々は特定して対処する必要がDate型の書式属性、クラスがDateSerializerは次のように実装宣言することができます。
class DateSerializer implements JsonSerializer<Date> {
SimpleDateFormat dateTime = new SimpleDateFormat("yyyy-MM-dd");
@Override
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(dateTime.format(src));
}
}
そして、Gsonは、次のように使用し、DateSerializerインスタンスを登録GsonBuilder使用を構築する前に、オブジェクト:
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).create();
したがって、Date型のフィールドのためのイベントのシーケンスは、によってカスタマイズされたときserialize
の日付における方法yyyy-MM-dd
以下のサンプルコードの形式の出力側:
@Test
void test_dateSerializer() {
MyObject myObject = new MyObject(new Date(), "one");
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).create();
String json = gson.toJson(myObject);
String exceptedJson = "{\"date\":\"2019-09-08\",\"name\":\"one\"}";
Assertions.assertEquals(exceptedJson, json); // true
}
class MyObject {
private Date date;
private String name;
public MyObject(Date date, String name) {
this.date = date;
this.name = name;
}
public MyObject() {
}
}
カスタム直列化復元
そして、カスタムシリアライズを使用すると、デシリアライズロジックをカスタマイズしたい、同様の方法を達成するために、JsonDeserializer抗実装するカスタムシリアライズロジックを行わ呼ばれる同じインターフェイスを実装する必要があります。例は、JSON文字列の内容があり{"CODE": 400, "MESSAGE": "参数错误"}
、フィールド名は、対応する変換を達成するために、同じではないので、前述の抗配列に結果オブジェクトである必要がある、次のように具体的に実現さResultDeserializerクラスをカスタマイズすることが必要です。
class ResultDeserializer implements JsonDeserializer<Result> {
@Override
public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
Result<Object> result = new Result<>(object.getAsJsonPrimitive("CODE").getAsInt(),object.getAsJsonPrimitive("MESSAGE").getAsString(), null);
return result;
}
}
次の例はGsonは、対応するオブジェクトおよびデシリアライゼーションを生成し、GsonBuilderの使用はResultDeserializerレジスタです。
@Test
void test_resultDeserializer() {
//language=JSON
String json = "{\"CODE\": 400,\"MESSAGE\": \"参数错误\"}";
Gson gson = new GsonBuilder().registerTypeAdapter(Result.class, new ResultDeserializer())
.create();
Result result = gson.fromJson(json, Result.class);
Assertions.assertEquals(400, result.code); // true
Assertions.assertEquals("参数错误", result.message); // true
}
Gson共通コメント
Gsonは、開発者が使用するためのAPIの一部を提供することに加えて、ノートの数が使用可能なプロパティをそこに持っている、それはGsonのコメントの中で最も人気が導入されました。
@Expose
このコメントは専用フィールドで使用することができ、役割がさらされた場合、対応するフィールドが直列化または非直列化されるかどうかを示すためにある、2つの特性を有するserialize
とdeserialize
デフォルトはtrueです。フィールドがコメントを追加するとき@Expose(serialize = true, deserialize = false)
は、このフィールドは、デシリアライゼーションは、シリアライズ時の割り当てを無視したときに見えるようにすることを意味しています。必要と追加のノートは、@ GsonBuilderの道限られてGsonを構築する場合にのみコメントを公開して、建物の前に呼び出されなければならない、ということであるexcludeFieldsWithoutExposeAnnotation
方法を、以下では、その使用の具体的な例は以下のとおりです。
@Test
void test_expose() {
MySubClass subclass = new MySubClass(42L, "the answer", "Verbose field not to serialize");
MyClass source = new MyClass(1L, "foo", "bar", subclass);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String s = gson.toJson(source);
System.out.println(s);
// {"id":1,"name":"foo","subclass":{"id":42,"description":"the answer","otherVerboseInfo":"Verbose field not to serialize"}}
}
@Data
@AllArgsConstructor
class MyClass {
private long id;
@Expose(serialize = false, deserialize = true)
private String name;
private transient String other;
@Expose
private MySubClass subclass;
}
@Data
@AllArgsConstructor
class MySubClass {
@Expose
private long id;
@Expose
private String description;
@Expose
private String otherVerboseInfo;
}
Gsonでで
transient
キーワードフィールドのデフォルトのシリアライズとデシリアライズを変更されることはありません、この動作は直列化およびJavaネイティブ操作のデシリアライズと一致しています。
@since
対応するフィールドのタイプまたはバージョンをマーキングするためのアノテーションは、バージョン番号はGsonシリアライゼーションおよびデシリアライゼーションに指定することができます。クラスに対応したWebサービスのJSONデータフィールドの複数のバージョンがある場合、この注釈は非常に参考になります。
同様に、唯一のために使用される注釈GsonBuilder
用いて、実施形態はGsonオブジェクトを構築setVersion
する方法が有効なバージョン、対応するフィールドオブジェクトのみ解析されたバージョンを指定し、以下では具体例です。
public class VersioningSupportTest {
@Test
void test() {
VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(versionedObject);
System.out.println(jsonOutput); // {"newField":"new","field":"old"}
}
}
class VersionedClass {
@Since(1.1)
private final String newerField;
@Since(1.0)
private final String newField;
private final String field;
public VersionedClass() {
this.newerField = "newer";
this.newField = "new";
this.field = "old";
}
}
@SerializedName
このアノテーションは、使用が簡単で、また有用です。@SerializedNameは、次のフィールドのメンバーの名前は、シリアル化され、使用、デシリアライズを指定する特定の使用であります:
public class JSONFieldNamingSupportTest {
private class SomeObject {
@SerializedName("custom_naming")
private final String someField;
private final String someOtherField;
public SomeObject(String a, String b) {
this.someField = a;
this.someOtherField = b;
}
}
@Test
void test() {
SomeObject someObject = new SomeObject("first", "second");
String jsonRepresentation = gson.toJson(someObject);
System.out.println(jsonRepresentation);
// {"custom_naming":"first","someOtherField":"second"}
SomeObject someObject1 = gson.fromJson(jsonRepresentation, SomeObject.class);
System.out.println(someObject1);
// SomeObject{someField='first', someOtherField='second'}
}
}
@JsonAdapter
上記のコメントとは異なり、@JsonAdapter
唯一のクラスに作用する、主な役割は、代わりのものでGsonBuilder.registerTypeAdapter
直接によって、方法を実行@JsonAdapter(aClass.class)
JsonDeserializerオブジェクトを指定するか、JsonSerializer実施例オブジェクト、同じ機能が未満の、優先度考えることができるGsonBuilder.registerTypeAdapter
のみであるため、優先度の高いですregisterTypeAdapter
実行は注釈方法を簡素化されるだろう、もはやプレゼンテーション、直前のテキストが存在しないカスタム直列化復元をクラスの結果を使用して効果を見ることができますが。
エピローグ
本論文では、研究のシリアライズとデシリアライズの操作はGsonフレームワークを使用してまとめたもので、機能Gsonの使用状況の様々なを紹介し、小さなパートナーが助けるためにJSONデータの頭痛に対処したいと考えています。
推奨読書
- 商品マスタ春ブートプロファイル
- どのようにエレガント近い春ブーツ応用
- あなたは見つけるためにインターフェイスを管理する必要がありますか?
- ジャワ、ロンボク島になります知っている必要があります
- Javaのマイクロサービスナコスの新世代
参考資料
- https://github.com/google/gson/blob/master/UserGuide.md
- https://www.jianshu.com/p/e740196225a4
- https://juejin.im/post/5aad29f8518825558453c6c9
- https://www.baeldung.com/gson-deserialization-guide
- https://www.baeldung.com/gson-string-to-jsonobject