2011-12-16 15 views
11

Okだから十分にはっきりしていなかったので質問を編集しました。GSON:カスタムオブジェクトの逆シリアル化

編集2:JSONファイルを更新しました。

私はAndroidアプリケーションでGSONを使用しています。サーバーからのJSONファイルを解析する必要があります。複雑すぎます。私はオブジェクトの構造が重すぎるようには望んでいないので、内容を簡略化したいと思います:私のオブジェクトの構造はJSONファイルの構造ではありません。

例えば、JSONに私はこれがあるとします。

{ 
    "object1":{ 
     "attribute1" : "test1", 
     "attribute40" : "test40", 
     "user":{ 
      "id":1, 
      "name":"foo" 
     } 
     ,"example":{ 
      "total":10, 
      "list":[ 
      { 
       "tag":"tag1", 
       "name":"object name 1", 
       "pos":1 
      }, 
      { 
       "tag":"tag10", 
       "name":"object name 10", 
       "pos":10 
      } 
     ] 
     } 
    } 
    "object2":{ 
     "attribute1":"test..." 
    } 
} 

私はArrayListが含まれている私の現在のオブジェクト構造、オブジェクトExample、で保存しないとint「合計」 。しかし、値が"object name 1;object name 2;..."の単純なStringだけを残したいと思います。

さらに、完全なユーザーではなくユーザーIDだけを保存したいと思っています。他のサーバーAPI呼び出しで完全なユーザーが既に別の場所に保管されているためです。

だから私のクラスのクラスは次のようになります:

class Foo{ 
    int userId; 
    String example; //"object name 1;object name 2;..." 
    ... 
} 

だから私たちは、カスタム・デシリアライザでこれを達成することができることを仮定し、私はどのように見つけることはありません。可能であればメモリを最小限に抑えたいので、完全なオブジェクトの例を持っていないと思って、それを使ってString exampleを構築するのは正しい方法です。

最悪の場合、複雑すぎると、サンプルオブジェクトを解析するときに、少なくともタグ項目のリストのみを格納できるようにしたいので、カスタムデシリアライザが必要です。total

だから私は持っているでしょう:

class Foo{ 
    int userId; 
    ArrayList<Tag> example; 
    ... 
} 
+0

GSONはあなたの特別な要件に応じて過剰です。あなたのドメインモデルのコンストラクタにJSON文字列を渡すだけで、単純な文字列操作を使って、必要なフィールドを分割/抽出します。 – yorkw

+0

確かに解決策かもしれませんが、毎回30個以上のフィールドを持つJSONファイルがたくさんあり、今後この構造が発展するかもしれません。だから、私は仕事とメンテナンスを最小限に抑えるために、GSONのようなライブラリを使うのが好きです。特に、特定のJSONファイルですでに使用している場合は特にそうです。 – Chayy

+0

"構造は将来的に進化するかもしれません"、OOの観点からは、すべてのプロパティを使用していなくても、完全なドメインオブジェクトをモデリングすることを強くお勧めします。 – yorkw

答えて

19

チャットで設計された完全なソリューションを提示し、変更されたJSON文字列に合わせるための答えを採用しました。このコードは、文字列jsonが質問から(更新された)JSONを正確に保持していることを前提としています。
を経由して、全体JSONを読み込み

  • GSON_DOM:(他のほとんどのREST-LIBSとして)

    class Object1 
    { 
        private String attribute1; 
        private String attribute40; 
        private int userId; 
        private String nameList; 
    } 
    

    GSONサポート三つのモード:要件は、次のクラス(省略さセッターとのtoString)を満たすことですJsonParser.parse()を呼び出し、メモリ内にDOMツリーを構築します(オブジェクトモデルアクセス)。したがって、このソリューションは小さなJSONファイルに適しています。

  • GSON_STREAM
    JsonReaderでJSONのチャンクのみを読み取ります。コードは複雑ですが、大規模なJSONファイルに適しています。 Android 3以降。0ハニカム、GSONのストリーミングパーサーはandroid.util.JsonReaderとして含まれています。
  • GSON_BIND
    反射によるクラスへのデータバインディングは、コードを有意義に最小限に抑えます。 GSONは混合モードを許可します。これは、この回答が示すべきGSON_DOMとGSON_BINDまたはGSON_STREAMとGSON_BINDを組み合わせることを意味します。

実装がどのように見えるGSON_DOMとGSON_BINDを経由して、クラスオブジェクト1を埋めるために:現時点では

:実装がどのように見えるGSON_STREAMとGSON_BINDを経由して、クラスオブジェクト1を満たすために

private static void deserializeViaObjectAccess(final String json) 
{ 
    Gson gson = new Gson(); 

    // Read the whole JSON into meomory via GSON_DOM 
    JsonParser parser = new JsonParser(); 
    JsonObject object1 = parser.parse(json).getAsJsonObject().getAsJsonObject("object1"); 

    // map the Object1 class via GSON_BIND 
    // (bind common attributes which exist in JSON and as properties in the class) 
    // mapper acts as factory 
    Object1 result = gson.fromJson(object1, Object1.class); 

    // manually read the attribute from the user object 
    int userId = object1.getAsJsonObject("user").getAsJsonPrimitive("id").getAsInt(); 
    result.setUserId(userId); 

    // manually read the attributes from the example object 
    String names = ""; 
    JsonArray list = object1.getAsJsonObject("example").getAsJsonArray("list"); 
    for (int i = 0; i < list.size(); ++i) 
    { 
     JsonObject entry = list.get(i).getAsJsonObject(); 
     String name = entry.getAsJsonPrimitive("name").getAsString(); 

     names = i == 0 ? name : names + "; " + name; 
    } 
    result.setNameList(names); 

    // Output the result 
    log.debug(result.toString()); 
} 

これは、ノードがGSON_BINDまたは GSON_STREAM経由で完全にロードされている場合にのみ可能です。この例では、ノード自体を分割する必要があります。これは、今後のバージョン2.2で可能なのは です。 GSON 2.2が利用可能になると、後でコードを渡します*

+0

2.2.4が最新のものです。GSON_BINDの使用方法を教えてください –

1

例を忘れて、あなたが欲しいものの文字列を構築するために例のオブジェクトの名前のプロパティを使用し、完全な例オブジェクトに例JSONをデシリアライズしますオブジェクト。

私は本当に2番目の質問を完全に理解していませんが、完全なTest1オブジェクトを持っている場合は、すべてのフィールド/プロパティを次にTest2オブジェクトを作成することができます。たとえば、Test2オブジェクトは、そのコンストラクタ内のパラメータとしてTest1を受け入れ、残りの部分を無視する必要があるプロパティのみを取ります。

+0

私の質問はもっと分かりました。私の構造に完全なオブジェクトを保存することなく、どうすればいいですか?例えば、私は 'gson.fromJson()'を実行することができます。これは、連結された文字列を除くすべてのフィールドを取得し、手動で連結します。 IDだけを保持するのと同じこと。しかし、私はこれらの役に立たないフィールドを記憶に残しておきたいと思っています。 2番目の質問では、2つのオブジェクト「Test1」と「Test2」は独立していません。オブジェクト「Test2」はオブジェクト「Test1」の前に作成できます。 – Chayy

1

httpを介してJsonsでストリーミングしている間は、テキストを破棄してカスタムオブジェクトのみを保存することができます。この場合、不要な情報は絶えず破棄されます。

While stream not empty 

    Read the next block into a new string 

    Deserialize the string to the your object 

    Store the object in a myArrayList 

注:アプリケーションが堅牢になりたい場合は、全体JSONを読み取り、それを消費するが、全体として、最も可能性が高い必要があります。生の文字ストリームとしてJSONを読んでいない限り(私の疑問は、あなたのJSONが実際には非常に大きく、このショートカットが必要でない限り)。

Neverthelssは、メモリに不要なデータ構造を書き込むことなく、入力ストリームを読み込み、JSONの整形式要件を満たすことができます。これは、データの小さなサブセットのみが必要な場合に機能します。JSONで人々の名前やURLだけを望むだけです。しかし、より複雑なデータ構造が必要な場合は、それが壊れてしまいます。それにもかかわらず:SQLベースあなたがいないgenerucallyおよび任意にすることができます

バール最終的に、Jsons REST要求好きではないされています

//例は

最終的な考えが

While input stream is not empty 

    String x = nextLine 

    If x contains "http" 

     myArrayList.add(parseUrl(x) 
全体のデータ構造を格納することなく、JSONラインからURLを解析しあなたが本当に軽量のJsonsを望むなら、より自然なアプローチは、あなたのREStサービスプロバイダがあなたのユースケースに対応するために単にRESt要求パラメータのタイプを広げることができるかどうかを伝えることです。

+0

この返答でOk xxは、私がまだ試していないアプローチです。しかし、私が試したObject Model Accessのように、このテクニックでは、フィールド名を手動で再度テストする必要があります。もちろんこれで私は不要な情報を捨てることができますが、さらに多くの作業とより難しいメンテナンスが必要です。あなたの方法が私の最初のものよりもパフォーマンスの点で優れているとしても、それは私のニーズに完全に一致しません。私はもっ​​と自動化されたものを探していました。私はあるオブジェクトに対して自分の仕事をし、他のフィールドはパーサで管理できるようにしました。 – Chayy

2

詳細は、hereというように、Gson内に構築されたパーサーを使用してJSON文字列を解析することです。

com.google.gson.JsonParser parser = new JsonParser(); 
JsonObject object = parser.parse(data).getAsJsonObject(); 
JsonObject example = object.getAsJsonObject("example"); 
JsonArray list = example.getAsJsonArray("list"); 

JsonObjectJsonArrayはGson自体の一部である:あなたはこのような何かをするだろう。

これらを使用した後、getAsIntなどの関数を使用して個々のフィールドを解析し、必要なオブジェクトをビルドして返すことができます。

編集1

それはこのexampleで与えられるよう、あなたもしていないカスタムクラスにちょうど一般的なJava型をgson.fromJsonを使用することができることのように思えるん。 したがって、parseを使用してJSON文字列を解析し、内部オブジェクトまたは配列の1つでfromJsonを呼び出す必要があります。

+0

これは、私がやった最初のアプローチであり、classicsのorg.jsonクラスです。このテクニックの欠点は、オブジェクトフィールドとJSONフィールドのマッチングを手動で行う必要があることです。私は30-40以上のフィールドを持っていると、多くの仕事と助けになります。 – Chayy

+0

内部クラスの1つでfromJsonも使用できるようです。これはあなたの必要性に合うかもしれません。 – Abhinav

0

Jacksonライブラリを使用することを提案します。これはApacheライセンスと共に提供されるため、商用では無料で使用できます。 tutorialでは、大文字の "Streaming API Example"を見てください。それは非常に簡単で、ストリーミングプロセスを完全に制御します。だから、あなたが望むものをとり、他のすべてのものを無視することができます。 ジャクソンの図書館はいくつかのジャーで分割されています。ストリーミングAPIをサポートするjarファイルは最小で、他のファイルは使用しません。 それは答えだと思います。

ジャクソンはさらに提供することができます。 this articleでは、上位レベルのJSONファイルを要素として読み取るメソッドがありますが、必要なオブジェクトと不要なオブジェクトをPREVIOUSLYで設定することができます。したがって、必要な要素だけをすぐに解析することができます。

+0

GSONライブラリは、たとえジャクソンの効率が悪いようであっても、ストリーミングモードを提供するようです。そして、とにかく私が他の人たちに言ったように、それは私のニーズに合っていません。 – Chayy

+0

JSONではなくGSONを意味しますか?とにかく、JSONオブジェクトを再構築する必要がありますが、必須フィールドだけを使用したいのですか?たぶん、あなたはあなたの質問を編集してください。質問はコメントの中で閉ざされてはならない。 – Gangnus

+0

あなたはJSONオブジェクトを再構築することができますが、必要なフィールドのみを使用することができます - それは私の答えの2番目の段落で話していることです。 – Gangnus

関連する問題