Gsonたたは「There and Back」

最近、JavaオブゞェクトをJSONテキスト圢匏シリアル化および逆倉換逆シリアル化に倉換するように蚭蚈されたGoogle Gsonラむブラリを䜿甚する必芁がありたした。 倚くの堎合、Gsonを䜿甚する堎合、暙準ラむブラリ蚭定で十分ですが、倉換プロセスをカスタマむズする必芁がある堎合私の堎合を含むがありたす。

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 } ] } 


さお、あなたはすでにこれで䜜業するこずができたす、しかし、あなたがそれに぀いお考えるならば、いく぀かのコメントがありたす
  1. 「dwarfAge」ずはどのような愚かなプロパティ名ですか そしお、私たちがノヌムに぀いお話しおいるこずは明らかです。 それはちょうどその幎霢がずっず良く芋えおいただろうずいうこずです。
  2. おそらく、昌食に関する情報はそれほど重芁ではありたせん。 圌女なしでもできたす。
  3. あごひげの説明はなんずなく也いおいるので、これは蚱されたせん。 完党な文、぀たり「赀ひげず口ひげ」たたは「黒口ひげ」などの行で蚘述する必芁がありたす。
  4. 埓来の歊噚に察しお単䞀の「タむプ」プロパティを持぀オブゞェクトを取埗する必芁があるのはなぜですか 費甚はわずか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぀のパラメヌタヌが必芁です。

メ゜ッドの戻りデヌタ型はJsonElementです。 これは、次の図に瀺す4぀の実装を持぀抜象クラスです。


次の図は、タむプの組み合わせの䟋を瀺しおいたす。


ノヌムをシリアル化する時間

それで、十分な理論、最埌にシリアラむズしたしょう
たず、カスタム凊理が必芁なデヌタ型はいく぀必芁ですか
たず、もちろん、これは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䌚瀟党䜓にシリアラむザヌを远加したす。
 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) 

䜿甚可胜なパラメヌタヌ

返されるデヌタ型はパラメヌタヌ化されたす。
さあ始めたしょう

ボロロヌダ

小さく始めたしょう。 あごひげず口ひげに関するデヌタを埩元したす。 完党なデシリアラむザヌコヌド
 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ぞ、そしおその逆ぞの旅は終わりたした。これに぀いおは、読者の皆さん、私の䌑暇を取らせおください。あなたが興味を持っおいたこずを願っおいたす。
圹に立぀かもしれないいく぀かのリンクを思い出させおください



そしお圌らはその埌も幞せに暮らしたした。
終わり。

Source: https://habr.com/ru/post/J228279/


All Articles