GSON:自定义对象反序列化

时间:2011-12-16 10:44:19

标签: java android 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“total”。但是我想只保留一个值为"object name 1;object name 2;..."的简单字符串。

此外,我只想存储用户ID,而不是完整的用户,因为我已经将完整的用户存储在其他地方,并且还有其他服务器API调用。

所以我的班级类似于:

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

所以我想我们可以使用自定义反序列化器实现这一点,但我找不到。我想尽可能减少内存,所以我不认为有一个完整的对象示例,然后使用它来构建我的String example是一种正确的方法。

在最坏的情况下,如果它太复杂了,我希望能够在解析Example Object时至少只存储Tag项列表:所以我需要一个自定义反序列化器来摆脱int {{ 1}}。

所以我会:

total

5 个答案:

答案 0 :(得分:20)

我采用了答案来呈现聊天中设计的完整解决方案,以适应更改后的JSON字符串。该代码假定字符串json完全包含问题中的(更新的)JSON。要求是填写以下类(setter和toString ommitted):

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

GSON支持(作为大多数其他REST-libs)三种模式:

  • GSON_DOM
    通过JsonParser.parse()读取整个JSON,并在内存中构建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和GSON_BIND填充Object1类,实现如下:

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和GSON_BIND填充Object1类,实现如下:

  

目前,这只有在通过GSON_BIND或者完全加载节点时才有可能   GSON_STREAM。此示例需要分割节点本身。这只是   即将推出的2.2版本可能。我稍后会把代码交给我   GSON 2.2可用。*

答案 1 :(得分: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而不仅仅是在example中给出的通用Java类型。 所以你必须使用parse解析你的JSON字符串,并在其中一个内部对象或数组上调用fromJson。

答案 2 :(得分:1)

将示例JSON序列化为完整的Example对象,使用Example对象的name属性构建所需事物的String,忘记Example对象。

我并不完全理解第二个问题,但是如果你有一个完整的Test1对象将所有的字段/属性,那么你可以创建一个Test2对象,它从Test1获取它想要的字段。例如,您的Test2对象可以在其构造函数中接受Test1作为参数,并仅采用它忽略其余部分所需的属性。

答案 3 :(得分: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中的网址。但如果您想要更复杂的数据结构,它就会崩溃。不过:

//示例解析来自JSON行的URL而不存储整个数据结构

While input stream is not empty

     String x = nextLine

     If x contains "http"

         myArrayList.add(parseUrl(x)

最后的想法:

最后,Jsons REST请求不像SQL那样 - 你不能在通用和任意地忽略某些领域。也许,如果你真的想要更轻量级的Jsons,更自然的方法是告诉或检查你的RESt服务提供商是否可以简单地扩大RESt请求参数的类型以适应您的用例。

答案 4 :(得分:0)

我建议使用Jackson库。它提供Apache许可证,因此您可以免费使用它进行商业用途。 在tutorial中查看首都“Streaming API Example”。它非常简单,您可以完全控制流式传输过程。所以,你可以拿走你想要的东西而忽略所有其他的东西。 杰克逊图书馆分为几个罐子。支持流API的jar是最小的,不使用任何其他jar。 我想是答案。

杰克逊可以提供更多。在this article中,您可以找到在更高级别上读取JSON文件的方法,作为元素,但前置设置您需要的对象和不需要的对象。因此,您可以立即解析所需的元素。