【问题标题】:Make Gson behavior depth-dependent使 Gson 行为依赖于深度
【发布时间】:2015-10-10 23:23:58
【问题描述】:

有没有一种理智的方法可以让 Gson 以不同于顶层的方式处理嵌套对象?要序列化的东西是实体,它们都有一个id。嵌套实体应替换为其id(以缩短输出并可能对抗无休止的递归)。

假设有一个

@AllArgsConstructor class User {
    int id;
    String name;
    User parent;
}

然后我执行

User grampa = new User(3, "Grampa", null);
User homer = new User(2, "Homer", grampa);
User bart = new User(1, "Bart", homer);

序列化bart时得到的是

{
    id: 1,
    name: "Bart",
    father: {
        id: 2,
        name: "Homer",
        father: {
            id: 3,
            name: "Grampa"
        }
    }
}

这比我需要的要多得多。我其实从不想序列化嵌套实体,它们的ids 已经足够好了。

使用 global 线程局部变量 hack 和 TypeAdapterFactory 我可以得到

{
    id: 1,
    name: "Bart",
    father: 2,
}

这几乎是我想要的。我更喜欢fatherId 而不是father,而且我肯定更喜欢比全局变量更简单的东西。

我不想创建 DAO、反射性地扫描对象或类似的方式(然后我可以自己完成整个序列化,不是吗?)。

【问题讨论】:

  • 我很难理解这种转变。感觉很多信息都消失了。
  • @SotiriosDelimanolis 现在好点了吗?
  • 你想要 Jackson 的 object identity 给 Gson 吗? Gson 扩展 GraphAdapterBuilder 让您可以做到这一点。

标签: java serialization gson


【解决方案1】:

我认为Gson documentation 指出了一种“更明智”的方法。创建一个 TypeAdapter 工厂,它封装状态并返回引用该状态的内部类 TypeAdapter 实例,如下所示:

public class UserTypeAdapterFactory {
    private Set<Integer> serializedUsers = new HashSet<>();

    public JsonSerializer<User> getTypeAdapter() {
        return (user, type, context) -> {
            JsonObject el = new JsonObject();
            el.addProperty("id", user.getId());
            el.addProperty("name", user.getName());

            if(user.getFather() != null) {
                JsonArray els = new JsonArray();
                int fatherId = user.getFather().getId();
                if(!serializedUsers.contains(fatherId)) {
                    JsonElement father = context.serialize(user.getFather());
                    if (father.isJsonArray()) {
                        els.addAll(father.getAsJsonArray());
                    } else {
                        els.add(father);
                    }
                    serializedUsers.add(fatherId);
                }
                el.addProperty("fatherId", fatherId);
                els.add(el);
                return els;
            } else {
                return el;
            }
        };
    }
}

Gson gson = new GsonBuilder()
    .registerTypeAdapter(
        User.class, 
        new UserTypeAdapterFactory().getTypeAdapter())
    .build();
System.out.println(gson.toJson(
    new User(1, "Bart", new User(2, "Homer", new User(3, "Grampa", null)))));

应该打印的内容:

[{"id":3,"name":"Grampa"},{"id":2,"name":"Homer","fatherId":3},{"id":1,"name":"Bart","fatherId":2}]

【讨论】:

  • 这很好,但它做了我想避免的事情,即我自己在田野上闲逛。我希望所有实体及其所有成员都这样做,而不枚举它们。 +++ 正如所写,它不能做预期的事情,因为serializedUsers 只经过测试和编写,但从未在其他地方使用。但我有这个想法,这可以解决。 +++ 它还使gson 实例上下文相关,并迫使我在每个请求/会话中使用它,而不是作为单例。但让我们称其为功能。
  • 这是一个概念证明。关键部分是上下文依赖。 set 是一个上下文,可让您知道是否需要序列化父亲。问题是序列化程序是在顶层注册的,这使得上下文是全局的。另一种选择是创建JsonElement 的代理,它允许本地上下文。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
  • 2012-04-30
  • 1970-01-01
相关资源
最近更新 更多