2011-12-16 189 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文件的方法作为元素,但先前设置了您需要的对象以及不需要的对象。因此,您可以立即解析所需的元素。