Gsonを使用した後、このチュートリアルを作成することにしました。このチュートリアルでは、例を使用してライブラリを操作する原理を説明します。 投稿は比較的長いことが判明しましたが、ストーリーの論理的一貫性のために分割したくありません。
最初に、サブジェクト領域を選択する必要があります。 どういうわけか、ノームの切り離しの考えが頭に浮かぶのかわかりません。 実際、なぜですか?
はい、記事に含まれるすべてのコードはGitHubで見つけることができます: https : //github.com/treble-snake/gson.dwarves
クラス図以外の画像は、 http: //www.javacreed.comのGsonに関する一連の記事から引用されています 。
はじめに
ドワーフについて
だから、「分隊」でそれは明らかです-これはある種のノームです。 しかし、ドワーフ自身はどうですか? ノームを特徴付ける最も重要な詳細は、もちろん、ひげです。 gnomeのひげの特徴と分類は長い間説明できますが、簡単にするために、gnomeに口ひげがあるかどうか、ひげがあるかどうか、およびそれらの色を定義します。 さらに、名前と年齢-それらなしで。 個人的な何かを追加し、小人が昼食を食べたと言います。 そして最後に、武器。 gnomeは多くの武器を持つことができ、シンプルにすることも、独自の名前と起源を持つユニークにすることもできます。
結果は次のようになります。
ドメインクラスの説明
簡潔にするために、1つのリストですべてのクラスを提供します。
public class DwarvesBand { List<Dwarf> dwarves = new LinkedList<>(); // getters & setters } public class Dwarf { private String name; private FacialHair facialHair; private List<Weapon> weapons = new LinkedList<>(); private String lunch; private int dwarfAge; public Dwarf() { } public Dwarf(String name, int dwarfAge) { this.name = name; this.dwarfAge = dwarfAge; } // getters & setters } /** * */ public class FacialHair { private boolean haveBeard; private boolean haveMustache; private String color; public FacialHair(boolean haveBeard, boolean haveMustache, String color) { this.haveBeard = haveBeard; this.haveMustache = haveMustache; this.color = color; } // getters & setters } public class Weapon { private String type; public Weapon() { // do nothing } public Weapon(String type) { this.type = type; } // getters & setters } public class UniqueWeapon extends Weapon { private String name; private String origin; public UniqueWeapon() { super(); } public UniqueWeapon(String type, String name, String origin) { super(type); this.name = name; this.origin = origin; } // getters & setters }
3人の参加者を追加してgnome会社を初期化します( すべてのキャラクターは架空のものであり、偶然の一致はランダムです )。
public class BandUtil { public static DwarvesBand createBand() { DwarvesBand company = new DwarvesBand(); Dwarf tmpDwarf; tmpDwarf = new Dwarf("Orin", 90); tmpDwarf.setLunch("Ale with chicken"); tmpDwarf.setFacialHair(new FacialHair(true, true, "black")); tmpDwarf.addWeapon(new UniqueWeapon("sword", "Slasher", "Gondolin")); tmpDwarf.addWeapon(new UniqueWeapon("shield", "Oaken Shield", "Moria")); tmpDwarf.addWeapon(new Weapon("dagger")); company.addDwarf(tmpDwarf); tmpDwarf = new Dwarf("Kori", 60); // no lunch :( tmpDwarf.setFacialHair(new FacialHair(false, true, "red")); tmpDwarf.addWeapon(new Weapon("mace")); tmpDwarf.addWeapon(new Weapon("bow")); company.addDwarf(tmpDwarf); tmpDwarf = new Dwarf("Billy Bob", 45); tmpDwarf.setLunch("Ale with chicken and potatoes, tea with some cakes"); tmpDwarf.setFacialHair(new FacialHair(false, false, "")); company.addDwarf(tmpDwarf); return company; } }
あそこ
デフォルトで
そのため、私たちはJSON形式でノームに関する情報を取得したいと考えています。 最も簡単な方法を試してみましょう-同じ名前のクラスのインスタンスを作成して
toJson()
メソッドを呼び出すことにより、Gsonライブラリの標準パラメーターを使用します。
DwarvesBand band = BandUtil.createBand(); Gson gson = new GsonBuilder() .setPrettyPrinting() .create(); String json = gson.toJson(band);
実際には、
Gson
クラスのインスタンスも
new
演算子を使用して作成
Gson
ますが、出力JSONはフォーマットされていないため、アプリケーション間でデータを交換するのに適しています(形成が速く、重量が少なくなります)が、人間の知覚には適していません。 したがって、特別なGsonBuilderを使用して
setPrettyPrinting()
メソッドを呼び出し、出力JSONを次の形式で表示できるようにしました。
デフォルトでシリアライズ後のJSON
{ "dwarves": [ { "name": "Orin", "facialHair": { "haveBeard": true, "haveMustache": true, "color": "black" }, "weapons": [ { "name": "Slasher", "origin": "Gondolin", "type": "sword" }, { "name": "Oaken Shield", "origin": "Moria", "type": "shield" }, { "type": "dagger" } ], "lunch": "Ale with chicken", "dwarfAge": 90 }, { "name": "Kori", "facialHair": { "haveBeard": false, "haveMustache": true, "color": "red" }, "weapons": [ { "type": "mace" }, { "type": "bow" } ], "dwarfAge": 60 }, { "name": "Billy Bob", "facialHair": { "haveBeard": false, "haveMustache": false, "color": "" }, "weapons": [], "lunch": "Ale with chicken and potatoes, tea with some cakes", "dwarfAge": 45 } ] }
さて、あなたはすでにこれで作業することができます、しかし、あなたがそれについて考えるならば、いくつかのコメントがあります:
- 「dwarfAge」とはどのような愚かなプロパティ名ですか? そして、私たちがノームについて話していることは明らかです。 それはちょうどその年齢がずっと良く見えていただろうということです。
- おそらく、昼食に関する情報はそれほど重要ではありません。 彼女なしでもできます。
- あごひげの説明はなんとなく乾いているので、これは許されません。 完全な文、つまり「赤ひげと口ひげ」または「黒口ひげ」などの行で記述する必要があります。
- 従来の武器に対して単一の「タイプ」プロパティを持つオブジェクトを取得する必要があるのはなぜですか? 費用はわずか1行です。
すべてのコメントを考慮に入れると、次の形式でgnomeに関する情報を表示できます。
{ "name": "Orin", "facialHair": "Black beard and mustache", "weapons": [ { "name": "Slasher", "origin": "Gondolin", "type": "sword" }, ... , "dagger" ], "age": 90 }
注釈
Gsonは、シリアル化を設定するための便利な注釈を提供します。 彼らが私たちを助けることができるかどうか見てみましょう。
最初の問題で-はい、 SerializedNameアノテーションをrespに追加することで、プロパティの出力名を変更できます。 クラスフィールド。 つまり、そうすることで:
@SerializedName("age") private int dwarfAge;
「dwarfAge」ではなく「age」という名前のプロパティを取得します。
すでに悪くありません、先に進みましょう。
lunch
フィールドを除外します。 まず、これにtransientキーワードを追加することでこれを行うことができます。この場合、シリアル化中にフィールドは考慮されません。 しかし、これが正しい方法であるという事実ではありません。 ここで昼食に関する情報が必要ないという事実は、他のシリアル化では必要ないという意味ではありません。
もう1つの方法は、 Exposeアノテーションを使用することです。 GsonBuilder.excludeFieldsWithoutExposeAnnotation()メソッドと連携してのみ機能します。このメソッドは、Exposeアノテーションを持たないすべてのフィールドの処理から除外します。 ただし、1つのフィールドを除外するには、他のすべてのフィールドに注釈を追加する必要があります。 便利すぎませんか?
カスタムシリアライザー
より柔軟な方法は、特定のタイプのオブジェクトをシリアル化する独自のクラスを作成することです。 これを行うには、 JsonSerializer <T>インターフェイスを実装する必要があります。Tは処理されたオブジェクトのタイプです。 単一のインターフェースの
serialize()
メソッドを考えてください:
JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context)
次の3つのパラメーターが必要です。
-
T src
実際には、シリアライズ可能なオブジェクト。 -
Type typeOfSrc
シリアライズ可能なオブジェクトのタイプ。 -
JsonSerializationContext context
-シリアル化コンテキスト。JsonSerializationContext
インターフェースも機能的であり、1つのメソッドとserialize()
が含まれています。 シリアル化可能なオブジェクトに含まれる非プリミティブデータを処理するために使用する必要があります(そして、それより少し低くします)。 コンテキストは、元のGsonオブジェクトのすべての設定(登録済みのシリアライザーなどを含む)を継承します。
メソッドの戻りデータ型は
JsonElement
です。 これは、次の図に示す4つの実装を持つ抽象クラスです。
-
JsonNull
実際にはnull
ビュー -
JsonPrimitive
文字列、数値などのプリミティブ型の表現 -
JsonArray
-JsonArray
型の多くのオブジェクト。List<JsonElement>
と考えることができます。 要素は任意のJsonElement
実装であり、混合型がサポートされています。 -
JsonObject
多数のキーと値のペア。ここで、キーは文字列であり、値は再びJsonElement
型のオブジェクトです。 構造Map<String, JsonElement>
似ています。
次の図は、タイプの組み合わせの例を示しています。
ノームをシリアル化する時間
それで、十分な理論、最後にシリアライズしましょう!
まず、カスタム処理が必要なデータ型はいくつ必要ですか?
まず、もちろん、これはgnome-
Dwarf
を記述するクラスです。
第二に、あごひげと口ひげのクラスは
FacialHair
です。
Weapon
、特に
UniqueWeapon
もここに起因しますが、今のところはデフォルトの処理に任せましょう。
したがって、
JsonSerializer
2つの実装が必要
JsonSerializer
。 それらは非常によく似ています:
public class DwarfSerializer implements JsonSerializer<Dwarf> { @Override public JsonElement serialize(Dwarf src, Type typeOfSrc, JsonSerializationContext context) { // ! return null; } } public class FacialHairSerializer implements JsonSerializer<FacialHair> { @Override public JsonElement serialize(FacialHair src, Type typeOfSrc, JsonSerializationContext context) { // ! return null; } }
Gsonがノームを処理するときにシリアライザーを使用するには、次のように
GsonBuilder
クラスの
registerTypeAdapter()
メソッドを使用して
registerTypeAdapter()
する必要があります。
Gson gson = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(Dwarf.class, new DwarfSerializer()) .registerTypeAdapter(FacialHair.class, new FacialHairSerializer()) .create();
ひげと口ひげ
ひげと口ひげの処理から始めることに気付きます。 以下に完全なコードを示します。詳細については、以下で説明します。
public class FacialHairSerializer implements JsonSerializer<FacialHair> { @Override public JsonElement serialize(FacialHair src, Type typeOfSrc, JsonSerializationContext context) { if (!src.isHaveBeard() && !src.isHaveMustache()) return new JsonPrimitive("is he really a dwarf?"); List<String> list = new LinkedList<String>(); if (src.isHaveBeard()) { list.add("beard"); } if (src.isHaveMustache()) { list.add("mustache"); } return new JsonPrimitive( new StringBuilder(src.getColor()) .append(" ") .append(StringUtils.join(list, " and ")) .toString() ); } }
すべてが非常に簡単です。 ひげと口ひげに関する情報を1行に減らすため、serialize()メソッドの結果は、目的の行を含む
JsonPrimitive
オブジェクトになります。
たとえば、gnomeにひげも口ひげもない場合、gnome属に対する態度を疑うことができます。
if (!src.isHaveBeard() && !src.isHaveMustache()) return new JsonPrimitive("is he really a dwarf?");
それ以外の場合、かなり簡単なアルゴリズムを使用して、ソースデータから必要な型の文字列を取得し、それに基づいて
JsonPrimitive
インスタンスを作成します。 そして、もちろん、入力オブジェクトと髪の色は、記事の教育目的にとってまったく重要ではないチェックでコードを複雑にしないために、常に初期化されると考えてみましょう。
ノーム自身
次に、gnomeの処理全体を実装します(チェックも省略します)。
public class DwarfSerializer implements JsonSerializer<Dwarf> { @Override public JsonElement serialize(Dwarf src, Type typeOfSrc, JsonSerializationContext context) { JsonObject result = new JsonObject(); result.addProperty("name", src.getName()); result.addProperty("age", src.getDwarfAge()); result.add("facialHair", context.serialize(src.getFacialHair())); JsonArray weapons = new JsonArray(); result.add("weapons", weapons); for(Weapon weapon : src.getWeapons()) { weapons.add( weapon instanceof UniqueWeapon ? context.serialize(weapon) : new JsonPrimitive(weapon.getType()) ); } return result; } }
このコードを部分的に解析しましょう。 結果としてJSONオブジェクトを取得する必要があるため、適切なタイプの変数を作成します。
JsonObject result = new JsonObject();
次に、 addProperty()メソッドを使用して、プリミティブ型のデータをオブジェクトに入力します(中間の
JsonPrimitive
オブジェクトを作成せずに)。 メソッドに2つのパラメーターを渡します。1つ目はキー、つまりJSONオブジェクトのプロパティの名前、2つ目は実際にはこのプロパティの値です。 ここでは、「dwarfAge」の代わりに「age」プロパティの名前を設定し、結果からランチ情報を除外します-結果オブジェクトに追加しません。
result.addProperty("name", src.getName()); result.addProperty("age", src.getDwarfAge());
次に、ひげに関するデータを追加する必要があります。 これを行うには、コンテキスト
serialize()
メソッドを使用します。前述のように、コンテキストは登録済みの
FacialHair
ため、
FacialHair
クラスには
FacialHairSerializer
FacialHair
適用されます。 目的のプロパティ名を指定して、 add()メソッドを使用して、結果の
JsonElement
をオブジェクトに追加します。
result.add("facialHair", context.serialize(src.getFacialHair()));
ノームの武器に関する情報を追加することだけが残っています。 武器のシンボリックキーがないため、
JsonArray
インスタンスを作成してそれらを保存し、同じadd()メソッドを使用してオブジェクトに追加します。
JsonArray weapons = new JsonArray(); result.add("weapons", weapons);
次に、作成した配列に要素を入力する必要があります。
JsonArray
クラスにはadd()メソッドもありますが、
JsonElement
型のパラメーターは1つだけで、論理的です。この場合、キーは必要ありません。 従来の武器を追加する場合、文字列に基づいて
JsonPrimitive
を作成し、コンテキストを使用して一意の武器をシリアル化します。 この場合、
UniqueWeapon
クラスのハンドラーを登録しなかったため、標準のシリアル化メカニズムが機能します。
weapons.add( weapon instanceof UniqueWeapon ? context.serialize(weapon) : new JsonPrimitive(weapon.getType()) );
結果
最後に、意図した目的のために労働の成果を使用します。
DwarvesBand band = BandUtil.createBand(); Gson gson = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(Dwarf.class, new DwarfSerializer()) .registerTypeAdapter(FacialHair.class, new FacialHairSerializer()) .create(); String json = gson.toJson(band);
取得したものを確認します。
出力JSON
{ "dwarves": [ { "name": "Orin", "age": 90, "facialHair": "black beard and mustache", "weapons": [ { "name": "Slasher", "origin": "Gondolin", "type": "sword" }, { "name": "Oaken Shield", "origin": "Moria", "type": "shield" }, "dagger" ] }, { "name": "Kori", "age": 60, "facialHair": "red mustache", "weapons": [ "mace", "bow" ] }, { "name": "Billy Bob", "age": 45, "facialHair": "is he really a dwarf?", "weapons": [] } ] }
最後のタッチ
変更したいのは、所有しているすべてのノームが「ドワーフ」プロパティに格納されている配列の要素であるということだけです。 これはどういうわけか堅実で冗長です。ノームについて話していることはわかっていますよね。 各gnomeをJSONオブジェクトの個別のプロパティとします。キーはgnomeの名前です。 例:
{ "Kori": { "age": 60, "facialHair": "red mustache", "weapons": [ ... ] }, ... }
ほとんどの場合、あなた自身がこの最終的なタッチに命を吹き込むために何をする必要があるか想像できます。 ただし、念のため:
実装
1. gnome会社全体にシリアライザーを追加します。
2.次の行を削除して、gnome
(
クラス)から名前情報を削除します。
3.
クラスの
メソッドへの呼び出しを追加して、分隊
ます。
public class DwarvesBandSerializer implements JsonSerializer<DwarvesBand> { @Override public JsonElement serialize(DwarvesBand src, Type typeOfSrc, JsonSerializationContext context) { JsonObject result = new JsonObject(); for(Dwarf dwarf : src.getDwarves()) { result.add(dwarf.getName(), context.serialize(dwarf)); } return result; } }
2.次の行を削除して、gnome
DwarfSerializer
(
DwarfSerializer
クラス)から名前情報を削除します。
result.addProperty("name", src.getName());
3.
GsonBuilder
クラスの
registerTypeAdapter()
メソッドへの呼び出しを追加して、分隊
GsonBuilder
registerTypeAdapter()
ます。
.registerTypeAdapter(DwarvesBand.class, new DwarvesBandSerializer())
そして、ノームの会社に必要なデータ形式を取得しました。
じゃあ!
{ "Orin": { "age": 90, "facialHair": "black beard and mustache", "weapons": [ { "name": "Slasher", "origin": "Gondolin", "type": "sword" }, { "name": "Oaken Shield", "origin": "Moria", "type": "shield" }, "dagger" ] }, "Kori": { "age": 60, "facialHair": "red mustache", "weapons": [ "mace", "bow" ] }, "Billy Bob": { "age": 45, "facialHair": "is he really a dwarf?", "weapons": [] } }
青い霧、白い霧のために安全に行くことができます!
戻る
JSONの冒険から戻ったgnomeチームは、自然に居心地の良いJavaオブジェクトに変身したいと思っています。 逆変換、つまり逆シリアル化のために、Gsonには
fromJson()
メソッドがあります。 2つのパラメーターを取ります。いくつかの形式のデータ(使用する
String
を含む)と返される結果の型です。 ただし、以下に示すように、単にGsonオブジェクトを作成してこのメソッドを呼び出すと、
DwarvesBand
の空のリストを持つ
DwarvesBand
クラスのインスタンスが取得されます。
DwarvesBand dwarvesBand = new Gson().fromJson(json, DwarvesBand.class);
変換には独自のアルゴリズムを使用し、デフォルトのGsonはフォーマットの処理方法を知らないため、これは自然なことです。 したがって、まったく同じ方法で、特殊なデシリアライザーを作成し、ライブラリーにノームに関する情報を処理するために使用する必要があることを伝える必要があります。 すでに推測したかもしれませんが、それらを作成するには、 JsonDeserializer <T>インターフェースとその唯一のdeserialize ()メソッドを実装する必要があります。
T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
使用可能なパラメーター:
-
JsonElement json
データの復元元のJson要素。 -
Type typeOfT
結果となるオブジェクトのタイプ。 -
JsonDeserializationContext context
-逆シリアル化コンテキスト。JsonSerializationContext
と同様に、JsonDeserializationContext
インターフェースには1つのJsonDeserializationContext
deserialize()
メソッドが含まれています。 このコンテキストは、gsonオブジェクトのすべての設定を継承します
返されるデータ型はパラメーター化されます。
さあ始めましょう!
ボロローダ!
小さく始めましょう。 あごひげと口ひげに関するデータを復元します。 完全なデシリアライザーコード:
public class FacialHairDeserializer implements JsonDeserializer<FacialHair> { @Override public FacialHair deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { FacialHair hair = new FacialHair(); String data = json.getAsString(); List<String> parts = Arrays.asList(data.split(" ")); if(parts.contains("beard")) hair.setHaveBeard(true); if(parts.contains("mustache")) hair.setHaveMustache(true); if(hair.isHaveBeard() || hair.isHaveMustache()) hair.setColor(parts.get(0)); return hair; } }
はい、良い方法で、入力データをより注意深くチェックする価値がありますが、例のコードを複雑にしないためにそれが正しいことは当然だと思います。
このメソッドの最も重要な行:
String data = json.getAsString();
getAsString()メソッドは、有効な文字列を含む
JsonPrimitive
型の要素、または
JsonArray
型のそのような要素を1つだけ含む
JsonPrimitive
適用される場合、
JsonElement
の内容を文字列に変換します。 そうでない場合、メソッドは例外をスローします。 フォーム
getAs{JavaType}()
すべてのメソッドは同様に機能します。
入力として文字列を
JsonPrimitive
を取得するので、これをチェックしません(
isJsonPrimitive()
メソッドを使用できます)。 取得したデータをさらに処理するのは簡単であり、私たちはそのデータにとどまりません。
ノーム
ノームのデータを復元する時が来ました。 このようにします:
public class DwafDeserializer implements JsonDeserializer<Dwarf> { @Override public Dwarf deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); Dwarf dwarf = new Dwarf(); dwarf.setDwarfAge(jsonObject.get("age").getAsInt()); dwarf.setFacialHair((FacialHair) context.deserialize(jsonObject.get("facialHair"), FacialHair.class)); JsonArray weapons = jsonObject.getAsJsonArray("weapons"); for(JsonElement weapon : weapons) { if(weapon.isJsonPrimitive()) { dwarf.addWeapon(new Weapon(weapon.getAsString())); } else { dwarf.addWeapon((UniqueWeapon) context.deserialize(weapon, UniqueWeapon.class)); } } return dwarf; } }
繰り返しますが、簡潔にするために一部のチェックは省略されています。 部分的に分析します。
gnomeに関する情報は
JsonObject
として表されることが
JsonObject
ているため、入力をチェックせずにこのタイプに変換します。
JsonObject jsonObject = json.getAsJsonObject();
年齢は整数型であるため、最初に
get()
メソッドを
JsonElement
年齢を
JsonElement
メソッドは、指定された「age」プロパティの値を持つ
JsonElement
を返し、次に
getAsInt()
メソッドを
getAsInt()
ます。
dwarf.setDwarfAge(jsonObject.get("age").getAsInt());
context.deserialize()
を使用して、ひげデータを
FacialHair
タイプのオブジェクトに復元します。 覚えているように、コンテキストはひげ情報を処理するために特別なデシリアライザーを使用する必要があることを認識しています。
dwarf.setFacialHair((FacialHair) context.deserialize(jsonObject.get("facialHair"), FacialHair.class));
Json配列の形式で「武器」プロパティの値をすぐに取得します。 最初にget(「武器」)メソッドで
JsonElement
を取得し、次に
isJsonArray()
メソッドを使用して配列の型を確認し、次に
isJsonArray()
メソッドを使用して配列に変換します。 しかし、私たちはノームとその入力の形式を信じています。
JsonArray weapons = jsonObject.getAsJsonArray("weapons");
武器のデータを復元するために、配列をウォークスルーします。
for(JsonElement weapon : weapons) { if(weapon.isJsonPrimitive()) { dwarf.addWeapon(new Weapon(weapon.getAsString())); } else { dwarf.addWeapon((UniqueWeapon) context.deserialize(weapon, UniqueWeapon.class)); } }
各要素について、
JsonPrimitive
タイプかどうかを確認します。 従来の武器は、このタイプに対応する単純な線で記述されていることを覚えています。 この場合、従来の武器のインスタンスを作成し、
getAsString()
メソッドを使用してそのタイプを取得します。 そうでなければ、私たちはユニークな武器を扱っています。 標準のGsonメカニズムを使用したコンテキストを使用して処理しました。
context.deserialize()
を使用して、同じことを実行します。
何かが足りないことに気づいた? そして、「何か」だけでなく、ノームの名前も! この重要な詳細を追加してgnome情報の回復を完了するには、最後のデシリアライザーに進みましょう。
デタッチメント
最後に、gnomeチーム全体のハンドラーを追加します。
public class DwarvesBandDeserializer implements JsonDeserializer<DwarvesBand> { @Override public DwarvesBand deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { DwarvesBand result = new DwarvesBand(); JsonObject jsonObject = json.getAsJsonObject(); for(Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) { Dwarf dwarf = context.deserialize(entry.getValue(), Dwarf.class); dwarf.setName(entry.getKey()); result.addDwarf(dwarf); } return result; } }
gnome処理と同様に
JsonObject
入力を
JsonObject
型に
JsonObject
ます。
JsonObject
は
Map<String, JsonElement>
として解釈できることは前に述べたのを
JsonObject
ますか?
Map
と同様に、
JsonObject
は、多くのキーと値の要素を返す
entrySet()
メソッドがあります。 彼の助けを借りて、ノームに関するすべての記録を一巡します。
要素の値は、名前を除く、gnomeに関するすべての情報です。 コンテキストを使用してこの情報を逆シリアル化し、Dwarfクラスのインスタンスを取得します。
Dwarf dwarf = context.deserialize(entry.getValue(), Dwarf.class);
空白のままになっている名前は、要素キーに含まれています。 これをオブジェクトに書き込むと、できればgnomeに関する情報が完全に復元されます!
dwarf.setName(entry.getKey());
ホームスイートホーム
焼きたてのデシリアライザーを登録することは残っており、「There and Back」の旅を始めることができます。 登録は、シリアライザーの登録とまったく同じです。
Gson gson = new GsonBuilder() .registerTypeAdapter(DwarvesBand.class, new DwarvesBandDeserializer()) .registerTypeAdapter(FacialHair.class, new FacialHairDeserializer()) .registerTypeAdapter(Dwarf.class, new DwafDeserializer()) .create();
テストするには、最初にノームの会社をJson文字列に変換し、次に元に戻し、明確にするために、標準のGsonメカニズムを使用して取得したJsonオブジェクトとして結果を出力します。 あなたは誰も忘れられず、何も忘れられないことを確認することができます、すべてのノームは安全で健全に戻りました!
検証コード
DwarvesBand company = BandUtil.createBand(); Gson gson; gson = new GsonBuilder() .registerTypeAdapter(Dwarf.class, new DwarfSerializer()) .registerTypeAdapter(FacialHair.class, new FacialHairSerializer()) .registerTypeAdapter(DwarvesBand.class, new DwarvesBandSerializer()) .create(); String json = gson.toJson(company); gson = new GsonBuilder() .registerTypeAdapter(DwarvesBand.class, new DwarvesBandDeserializer()) .registerTypeAdapter(FacialHair.class, new FacialHairDeserializer()) .registerTypeAdapter(Dwarf.class, new DwafDeserializer()) .create(); DwarvesBand bandIsBack = gson.fromJson(json, DwarvesBand.class); gson = new GsonBuilder() .setPrettyPrinting() .create(); System.out.println(gson.toJson(bandIsBack));
結果
{ "dwarves": [ { "name": "Orin", "facialHair": { "haveBeard": true, "haveMustache": true, "color": "black" }, "weapons": [ { "name": "Slasher", "origin": "Gondolin", "type": "sword" }, { "name": "Oaken Shield", "origin": "Moria", "type": "shield" }, { "type": "dagger" } ], "dwarfAge": 90 }, { "name": "Kori", "facialHair": { "haveBeard": false, "haveMustache": true, "color": "red" }, "weapons": [ { "type": "mace" }, { "type": "bow" } ], "dwarfAge": 60 }, { "name": "Billy Bob", "facialHair": { "haveBeard": false, "haveMustache": false, "color": "" }, "weapons": [], "dwarfAge": 45 } ] }
両方向に
そのため、「There」(JavaからJSONへ)と「Back」(JSONからJavaへ)の旅を調べました。
JsonElement
とデ
JsonElement
、
JsonElement
親切に提供してくれた
JsonElement
ようなオブジェクトの中間層で作業しました。
また、非常に便利ですが、オーバーヘッドにつながります。 Gsonは、中間層を削除することでパフォーマンスの利便性を犠牲にする機会を与えてくれます。これを行うには、カスタム変換にJsonSerializer + JsonDeserializerのペアではなく、両方向に変換するように設計されたTypeAdapter <T>クラスの実装を使用します。私たちが最も興味を持っているのは、このクラスの2つの抽象メソッド-
write()
と
read()
です。これらは、カスタム変換に責任があります。-シリアル
write()
化、および
read()
-シリアル化解除。
デフォルトで、gnomeの武器を独自のデバイスに投げたことを覚えていますか?この不正を修正しましょう。 「ゴンドラの斬撃兵」のような行に武器の名前と起源を組み合わせます。そして、ささいなことをしないために、私たちは作成します
TypeAdapter
ユニークなアイテムだけでなく、武器のリスト全体に対して。クラスは次のようになります。
public class WeaponsTypeAdapter extends TypeAdapter<List<Weapon>> { @Override public void write(JsonWriter out, List<Weapon> value) throws IOException { // Java → JSON } @Override public List<Weapon> read(JsonReader in) throws IOException { // JSON → Java return null; } }
さて、古いスキームによれば、メソッドを呼び出すことにより、武器リストの新しいハンドラーをGsonに通知する必要があり
.registerTypeAdapter()
ます。ただし、障害があります。-メソッドの最初のパラメータは、私たちが通常のリストを販売し、ハンドラが登録されているデータ型で、GNOMEの武器
List<Weapon>
。また、他のすべてのリストがTypeAdapterによって処理されることは望ましくありません。パラメータ化された型を渡して、武器のリスト専用であることを何らかの形で示す必要があります。これを行うために、Gsonは特別なトリッキーなクラス-TypeToken <T>を使用します。これにより、次のように必要なパラメーター化された型を取得できます。
Type weaponsListType = new TypeToken<List<Weapon>>(){}.getType();
実際、
TypeToken
匿名クラスによってパラメーター化されたクラスを特別に継承しているため
getGenericSuperclass()
、メソッドの親をパラメーター化する型を取得できます。この場合、親をパラメーター化する型はours
List<Weapon>
です。少々混乱しますが、別の方法で、残念ながら何もありません。たとえば、この記事では、汎用クラスのパラメーターの取得について詳しく読むことができます。
よく、さらに-通常どおり:
Type weaponsListType = new TypeToken<List<Weapon>>(){}.getType(); Gson gson = new GsonBuilder() .setPrettyPrinting() .registerTypeAdapter(Dwarf.class, new DwarfSerializerWithTypeAdapter()) .registerTypeAdapter(FacialHair.class, new FacialHairSerializer()) .registerTypeAdapter(DwarvesBand.class, new DwarvesBandSerializer()) .registerTypeAdapter(weaponsListType, new WeaponsTypeAdapter()) .create();
gnomeのシリアル化とシリアル化解除のコードを変更するだけで、処理された値のタイプを示すコンテキストに武器の処理の制御を渡します。
public class DwarfSerializerWithTypeAdapter implements JsonSerializer<Dwarf> { public JsonElement serialize(...) { ... Type weaponsType = new TypeToken<List<Weapon>>(){}.getType(); result.add("weapons", context.serialize(src.getWeapons(), weaponsType)); ... } } public class DwafDeserializerWithTypeAdapter implements JsonDeserializer<Dwarf> { public Dwarf deserialize(...) { ... Type weaponsType = new TypeToken<List<Weapon>>(){}.getType(); List<Weapon> weapons = context.deserialize(jsonObject.getAsJsonArray("weapons"), weaponsType); dwarf.addWeapons(weapons); ... } }
以上で、アダプターは接続されました。ああ、それを実現するために残っています。いつものように、ネタバレの下には完全なコードがあります。これについては、以下で詳細に分析します。
完全なTypeAdapterコード
public class WeaponsTypeAdapter extends TypeAdapter<List<Weapon>> { @Override public void write(JsonWriter out, List<Weapon> value) throws IOException { out.beginArray(); for (Weapon weapon : value) { if (weapon instanceof UniqueWeapon) { UniqueWeapon uWeapon = (UniqueWeapon) weapon; out.beginObject(); out.name("name") .value(uWeapon.getName() + " from " + uWeapon.getOrigin()); out.name("type") .value(uWeapon.getType()); out.endObject(); } else { out.value(weapon.getType()); } } out.endArray(); } @Override public List<Weapon> read(JsonReader in) throws IOException { List<Weapon> result = new LinkedList<>(); in.beginArray(); while (in.hasNext()) { switch (in.peek()) { case STRING: result.add(createCommonWeapon(in)); break; case BEGIN_OBJECT: result.add(createUniqueWeapon(in)); break; default: in.skipValue(); break; } } return result; } private Weapon createCommonWeapon(JsonReader in) throws IOException { return new Weapon(in.nextString()); } private Weapon createUniqueWeapon(JsonReader in) throws IOException { UniqueWeapon weapon = new UniqueWeapon(); in.beginObject(); while (in.hasNext()) { switch (in.nextName()) { case "name": String[] tmp = in.nextString().split(" from "); weapon.setName(tmp[0]); if (tmp.length > 1) weapon.setOrigin(tmp[1]); break; case "type": weapon.setType(in.nextString()); break; default: in.skipValue(); break; } } in.endObject(); return weapon; } }
そして再び
そのため、メソッドはそこで変換を行います
write()
。彼のコードは:
public void write(JsonWriter out, List<Weapon> value) throws IOException { out.beginArray(); for (Weapon weapon : value) { if (weapon instanceof UniqueWeapon) { UniqueWeapon uWeapon = (UniqueWeapon) weapon; out.beginObject(); out.name("name") .value(uWeapon.getName() + " from " + uWeapon.getOrigin()); out.name("type") .value(uWeapon.getType()); out.endObject(); } else { out.value(weapon.getType()); } } out.endArray(); }
メソッドのパラメーターには、JsonWriterクラスのインスタンスと武器のリストがあります。
JsonWriter
ストリーミングモードで出力JSONを作成できます。まず、武器データを保存する配列が必要です。
out.beginArray(); ... out.endArray();
実際、これらのコマンドは、角括弧を配置する役割を果たします(実際、JSONの配列が指定されているため)。出力で配列を取得したいので、メソッドの最初で開始し、最後で終了します。ここではすべてが非常に簡単です。同様に、メソッド<codebeginObject()</ codeおよび<codeendObject()</ codeを使用してオブジェクトを作成します。
さらに、従来の武器の場合、メソッドを呼び出すことにより、単純にプリミティブ型(文字列)の値を配列に書き込みます
value()
。
out.value(weapon.getType());
そして、ユニークな武器の場合、オブジェクトを作成し、2つのキーと値のペアを書き込み、交互に
name()
とメソッドを呼び出します
value()
。
out.name("name") .value(uWeapon.getName() + " from " + uWeapon.getOrigin()); out.name("type") .value(uWeapon.getType());
それだけです、武器の配列が記録されます。
そして再び
武器を混合データ型のJSON配列にかなり威勢よく変換しましたか?そして今、それを元に戻す時です。そして、ここで小さな問題が待っています。そのため、メソッド
read()
は1つのパラメーターを取ります。
public List<Weapon> read(JsonReader in) throws IOException {...}
JsonReaderクラスは、Jsonからデータを抽出するほか、ストリーム形式でも使用します。したがって、すべての「ノード」を一貫してソートし、それに応じて処理する必要があります。
記録と同様に、オブジェクトと配列は
beginObject() / endObject()
およびメソッドによって処理されます
beginArray() / endArray()
。
オブジェクトのプロパティは、メソッド
nextName()
、値-メソッド
next{Type}()
(たとえば、
nextString()
)でソートします。配列要素もメソッドによって繰り返され
next{Type}()
ます。
ただし、特定の要素のシーケンスを使用した厳密なデータ形式を使用している場合、これはすべて良好です。そして、いつ配列を開くか、いつオブジェクトを開くかなどがわかります。このケースでは、Jsonオブジェクトと文字列を任意の順序で配置できる配列の混合データ型を扱っています。幸いなことに
GsonReader
また
peek()
、次のノードのタイプを処理せずに返すメソッドもあります。
したがって、メソッドの一般的なビューは次の
read()
ようになります。
@Override public List<Weapon> read(JsonReader in) throws IOException { List<Weapon> result = new LinkedList<>(); in.beginArray(); while (in.hasNext()) { switch (in.peek()) { case STRING: result.add(createCommonWeapon(in)); break; case BEGIN_OBJECT: result.add(createUniqueWeapon(in)); break; default: in.skipValue(); break; } } in.endArray(); return result; }
gnomeの兵器庫は、オブジェクト(一意のインスタンスの場合)と文字列(通常のインスタンスの場合)を含む配列で表されることがわかっています。したがって、配列の各要素を処理して、この要素の初期ノードのタイプを確認します。文字列とオブジェクトを処理するために、呼び出すメソッドを作成しました。他のタイプはメソッドによって単にスキップされ
skipValue()
ます。
従来の武器を作成する方法は非常に簡単です。
private Weapon createCommonWeapon(JsonReader in) throws IOException { return new Weapon(in.nextString()); }
メソッド
nextString()
を使用して武器のタイプを含む行を取得し、それに基づいてオブジェクトを作成します。
ユニークな武器で-もう少し複雑:
private Weapon createUniqueWeapon(JsonReader in) throws IOException { UniqueWeapon weapon = new UniqueWeapon(); in.beginObject(); while (in.hasNext()) { switch (in.nextName()) { case "name": String[] tmp = in.nextString().split(" from "); weapon.setName(tmp[0]); if (tmp.length > 1) weapon.setOrigin(tmp[1]); break; case "type": weapon.setType(in.nextString()); break; default: in.skipValue(); break; } } in.endObject(); return weapon; }
オブジェクトに入り、メソッドを使用してそのすべてのプロパティを反復処理します
nextName()
。「name」および「type」という名前のプロパティには、処理アルゴリズムがあります-それらに基づいて、従来のユニークな武器のインスタンスを作成します。残りのプロパティ(ある場合)も、スキップされます。
したがって、TypeAdapterを使用したgnomeの兵器のシリアル化解除の準備ができました。
念のため、すべてが正常かどうかを確認します。
検証コード
DwarvesBand company = BandUtil.createBand(); Gson gson; Type weaponsType = new TypeToken<List<Weapon>>(){}.getType(); gson = new GsonBuilder() .registerTypeAdapter(Dwarf.class, new DwarfSerializerWithTypeAdapter()) .registerTypeAdapter(FacialHair.class, new FacialHairSerializer()) .registerTypeAdapter(DwarvesBand.class, new DwarvesBandSerializer()) .registerTypeAdapter(weaponsType, new WeaponsTypeAdapter()) .setPrettyPrinting() .create(); String json = gson.toJson(company); System.out.println("Serialized:"); System.out.println(json); gson = new GsonBuilder() .registerTypeAdapter(DwarvesBand.class, new DwarvesBandDeserializer()) .registerTypeAdapter(FacialHair.class, new FacialHairDeserializer()) .registerTypeAdapter(Dwarf.class, new DwafDeserializerWithTypeAdapter()) .registerTypeAdapter(weaponsType, new WeaponsTypeAdapter()) .create(); DwarvesBand companyIsBack = gson.fromJson(json, DwarvesBand.class); gson = new GsonBuilder() .setPrettyPrinting() .create(); System.out.println("\n\nDeserialized:"); System.out.println(gson.toJson(companyIsBack));
結果
Serialized: { "Orin": { "age": 90, "facialHair": "black beard and mustache", "weapons": [ { "name": "Slasher from Gondolin", "type": "sword" }, { "name": "Oaken Shield from Moria", "type": "shield" }, "dagger" ] }, "Kori": { "age": 60, "facialHair": "red mustache", "weapons": [ "mace", "bow" ] }, "Billy Bob": { "age": 45, "facialHair": "is he really a dwarf?", "weapons": [] } } Deserialized: { "dwarves": [ { "name": "Orin", "facialHair": { "haveBeard": true, "haveMustache": true, "color": "black" }, "weapons": [ { "name": "Slasher", "origin": "Gondolin", "type": "sword" }, { "name": "Oaken Shield", "origin": "Moria", "type": "shield" }, { "type": "dagger" } ], "dwarfAge": 90 }, { "name": "Kori", "facialHair": { "haveBeard": false, "haveMustache": true, "color": "red" }, "weapons": [ { "type": "mace" }, { "type": "bow" } ], "dwarfAge": 60 }, { "name": "Billy Bob", "facialHair": { "haveBeard": false, "haveMustache": false, "color": "" }, "weapons": [], "dwarfAge": 45 } ] }
あとがき
したがって、JavaからJSONへ、そしてその逆への旅は終わりました。これについては、読者の皆さん、私の休暇を取らせてください。あなたが興味を持っていたことを願っています。
役に立つかもしれないいくつかのリンクを思い出させてください:
そして彼らはその後も幸せに暮らしました。
終わり。