2011-12-16 192 views
11

好的,我編輯了這個問題,因爲它不夠清楚。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..." 
    } 
} 

我不想讓我的當前對象的結構,對象Example,一個包含ArrayListint「總」 。但我想只保留一個簡單的字符串,其值爲"object name 1;object name 2;..."

此外,我想只存儲用戶ID,而不是完整的使用,因爲我已經有存儲在別處完整的用戶,與其他服務器API調用。

所以我班班會是這樣的:

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

所以我想,我們可以用自定義解串器實現這一點,但我不覺得怎樣。我想盡可能減少內存,所以我不認爲有一個完整的對象的例子,然後用它來構建我的String example是一個正確的方法。

在最壞的情況下,如果它太複雜了,我希望能夠在解析示例對象時至少存儲標籤項列表:所以我需要一個自定義解串器來擺脫int total

因此,我將有:

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

根據您的特殊要求,GSON是過度技術。只需將您的JSON字符串傳遞到您的域模型的構造函數中,然後使用純字符串操作split/extract所需的字段。 – yorkw 2012-01-04 20:48:01

+0

嗯,確定它可能是一個解決方案,但是我有很多JSON文件,每次有超過30個字段,並且結構可能會在未來發展。所以,我肯定會更喜歡使用像GSON這樣的庫來減少工作和維護。特別是如果我已經在某些JSON文件上使用它。 – Chayy 2012-01-06 09:31:14

+0

「結構可能會在未來發展」,從OO的角度來看,強烈建議現在對完整的域對象進行建模,即使您沒有使用它的所有屬性。 – yorkw 2012-01-08 22:18:52

回答

19

我採用了答案來呈現在聊天中設計的完整解決方案,以適應已更改的JSON字符串。該代碼假定字符串json完全包含問題中的(更新的)JSON。要求是填補下面的類(設定器和toString ommitted):

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

GSON支撐件(作爲最其他REST-庫)三種模式:

  • GSON_DOM
    通過讀取整個JSON JsonParser.parse()並在內存中構建DOM樹(對象模型訪問)。因此,該解決方案適用於小型JSON文件。
  • GSON_STREAM
    通過JsonReader只讀取JSON的塊。代碼更復雜,但它適用於大型JSON文件。截至Android 3。0 Honeycomb,GSON的流解析器包含在android.util.JsonReader中。
  • GSON_BIND
    通過反射直接將數據綁定到類,最大限度地減少代碼。 GSON允許混合模式,這意味着結合GSON_DOM和GSON_BIND或GSON_STREAM和GSON_BIND,這個答案應該顯示。

要通過GSON_DOM填充類Object1和GSON_BIND實施的樣子:

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_STREAM類Object1和GSON_BIND實施的樣子:

目前,只有通過GSON_BIND或 GSON_STREAM完全裝入節點時纔可能。這個例子需要一個節點本身應該被分割。這是隻有 可能與即將到來的版本2.2。我會在稍後提供代碼 GSON 2.2可用。*

1

反序列化的例子JSON成一個完整的實例對象,請使用實例對象的名稱屬性來打造你想要的東西一個字符串,忘掉例目的。

我真的不明白的第二個問題完全,但如果你有一個完整的Test1對象將所有字段/屬性,那麼你可以創建一個對象的Test2這需要從它想要的Test1的字段。例如,你的Test2對象可以接受Test1作爲其構造函數中的一個參數,並且只取其所需的屬性而忽略其餘部分。

+0

好,所以我的問題更多:我怎麼做,而不必在我的結構中存儲完整的對象。例如,我可以執行`gson.fromJson()`,它將獲得除我的連接字符串之外的所有字段,然後手動連接。同樣的事情,只保留一個ID。但我不想在內存中存在這些無用的字段,我想在序列化過程中這樣做。對於第二個問題,沒有兩個對象`Test1`和`Test2`是獨立的:對象`Test2`可以在對象`Test1`之前創建。 – Chayy 2011-12-16 14:36:11

1

當你在Jsons了HTTP流,你可以簡單地丟棄文本,只存儲您的自定義對象。在這種情況下,您將不斷丟棄不需要的信息。

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中的網址。但如果你想要更復雜的數據結構,它會崩潰。儘管如此:

//示例解析來自JSON線網址而不存儲整個數據結構

While input stream is not empty 

    String x = nextLine 

    If x contains "http" 

     myArrayList.add(parseUrl(x) 

最後的想法:

伯爾最終Jsons REST請求不像SQL-你不能generucally和任意忽略某些領域。或許,如果你真的想要輕量級的Jsons,更自然的方法是告訴或者檢查你的RESt服務提供者是否可以簡單地擴展RESt請求參數的類型以適應你的用例。

+0

好的thx這個答案,這是一種方法,我還沒有嘗試。但作爲我嘗試的對象模型訪問,使用這種技術,您需要再次手動測試每個字段的字段名稱。當然,我可以放棄這些不必要的信息,但是它也需要更多的工作,並且維護更加困難。即使你的方法在性能方面比我的第一個方法更好,但它並不完全符合我的需求。我在尋找更自動的東西,我可以在一個對象上完成特定的工作,並讓其他字段由解析器管理。 – Chayy 2012-01-09 10:16:46

2

其中一個選擇是使用Gson內部的解析器解析JSON字符串,詳細爲here。你會做這樣的事情:

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

它似乎就像你可以在自定義類使用gson.fromJson也並不僅僅是普通的Java類型,如本example給出。 所以你必須使用parse解析你的JSON字符串,並在其中一個內部對象或數組上調用fromJson。

+0

好的,所以這是我做的第一個方法,用經典的org.json類。這種技術的缺點是我手動必須在我的對象字段和JSON字段之間進行匹配。當我有超過30-40個領域時,這是很多工作和維護。 – Chayy 2012-01-09 10:22:01

0

我會建議使用Jackson庫。它提供了Apache許可證,因此您可以免費將其用於商業用途。 在tutorial看大寫「Streaming API Example」。這是非常容易的,你完全控制流媒體過程。所以,你可以採取你想要的而忽略所有其他的東西。傑克遜圖書館分爲幾個罐子。支持流API的jar是最小的,不會使用任何其他的。 這是我想的答案。

傑克遜可以提供更多。在this article中,您可以找到以更高級別讀取JSON文件的方法作爲元素,但先前設置了您需要的對象以及不需要的對象。因此,您可以立即解析所需的元素。