【问题标题】:GSON : custom object deserializationGSON:自定义对象反序列化
【发布时间】:2013-12-05 04:36:38
【问题描述】:

好的,所以我编辑了这个问题,因为它不够清楚。

编辑 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,其中包含一个ArrayList 和一个int“总计”。但我只想保留一个简单的字符串,其值为"object name 1;object name 2;..."

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

所以我的班级应该是这样的:

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

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

在最坏的情况下,如果它太复杂,我希望能够在解析示例对象时至少只存储标记项的列表:所以我需要一个自定义反序列化器来摆脱 int @987654328 @。

所以我会:

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

【问题讨论】:

  • 根据您的特殊要求,GSON是矫枉过正。只需将 JSON 字符串传递到域模型的构造函数中,然后使用纯字符串操作拆分/提取所需字段。
  • 嗯,当然可以,但是我有很多JSON文件,每次都有超过30个字段,将来结构可能会演变。所以,我肯定更喜欢使用像 GSON 这样的库来最大限度地减少工作和维护。特别是如果我已经在某些 JSON 文件上使用它。
  • “结构可能在未来发展”,从面向对象的角度来看,强烈建议现在对完整的域对象进行建模,即使您不使用它的所有属性。

标签: java android gson


【解决方案1】:

我采用了这个答案来展示在聊天中设计的完整解决方案并适应更改后的 JSON 字符串。该代码假定字符串 json 恰好包含问题中的(更新的)JSON。要求是填写以下类(setter 和 toString 省略):

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

GSON 支持(与大多数其他 REST 库一样)三种模式:

  • 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 或 GS​​ON_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 可用。*

【讨论】:

  • 2.2.4 是最新版本,请告诉我们如何使用 GSON_BIND
【解决方案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。

【讨论】:

  • 好的,这是我使用经典 org.json 类的第一种方法。这种技术的缺点是我必须手动在我的对象字段和 JSON 字段之间进行匹配。当我有超过 30-40 个字段时,需要大量的工作和维护。
  • 似乎您也可以在其中一个内部类中使用 fromJson 。这可能适合您的需要。
【解决方案3】:

将示例 JSON 反序列化为完整的示例对象,使用示例对象的名称属性构建所需内容的字符串,忘记示例对象。

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

【讨论】:

  • 好的,所以我的问题更多:我怎样才能做到这一点,而不必在我的结构中存储完整的对象。例如,我可以执行gson.fromJson(),它将获取除连接字符串之外的所有字段,然后手动执行连接。同样的事情只保留一个ID。但是我不想在内存中有这些无用的字段,我想在序列化过程中这样做。对于第二个问题,Test1Test2 这两个对象不是独立的:对象Test2 可以在对象Test1 之前创建。
【解决方案4】:

当您通过 http 在 Json 中进行流式传输时,您可以简单地丢弃文本并仅存储您的自定义对象。在这种情况下,您将不断丢弃不需要的信息。

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。但如果你想要更复杂的数据结构,它就会崩溃。尽管如此:

// 示例从 JSON 行中解析 url,而不存储整个数据结构

While input stream is not empty

     String x = nextLine

     If x contains "http"

         myArrayList.add(parseUrl(x)

最后的想法:

Bur 归根结底,Json 的 REST 请求不像 SQL——你不能普遍和任意地忽略某些字段。也许,如果你真的想要更轻量的 Json,更自然的方法是告诉或检查你的 REST 服务提供商是否可以简单地扩展REST 请求参数的类型以适应您的用例。

【讨论】:

  • 好吧,谢谢这个答案,这是一种我还没有尝试过的方法。但是作为我尝试的对象模型访问,使用这种技术,您需要再次手动测试每个字段的字段名称。当然有了这个我可以丢弃不需要的信息,但它也需要更多的工作,更难维护。即使您的方法在性能方面比我的第一个方法更好,它也不能完全符合我的需求。我一直在寻找更自动化的东西,我可以在一个对象上完成我的特定工作,而让其他字段由解析器管理。
【解决方案5】:

我建议使用Jackson 库。它提供 Apache 许可证,因此您可以免费将其用于商业用途。 在tutorial 中查看大写的“Streaming API Example”。这很容易,您可以完全控制流式传输过程。所以,你可以拿走你想要的,而忽略所有其他的东西。 杰克逊图书馆分为一些罐子。支持流式 API 的 jar 是最小的,不使用任何其他的。 我想这就是答案。

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

【讨论】:

  • GSON 库似乎也提供了流模式,即使它看起来不如 Jackson.无论如何,正如我对其他人所说的那样,它不适合我的需要。
  • 您的意思是 GSON,而不是 JSON,不是吗?无论如何,您想要重建 JSON 对象,但只包含必填字段?也许,你应该重新编辑你的问题。这个问题不应该在 cmets 中被关闭。
  • 您可以重建 JSON 对象,但只有必填字段 - 这正是我在回答的第二段中所说的内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多