【问题标题】:How to implement a Gson equivalent of @JsonUnwrap如何实现与@JsonUnwrap 等效的 Gson
【发布时间】:2017-04-08 13:26:16
【问题描述】:

我知道 Gson 没有类似的功能,但是有没有办法像 @JsonUnwrap 那样添加对解包 Json 字段的支持?

目标是允许这样的结构:

public class Person { 
    public int age; 
    public Name name; 
} 

public class Name { 
    public String first;
    public String last; 
}

被(反)序列化为:

{
  "age" : 18, 
  "first" : "Joey", 
  "last" : "Sixpack" 
}

代替:

{
   "age" : 18, 
    "name" : {
        "first" : "Joey",
        "last" : "Sixpack"
    } 
 }

我知道它可能会变得相当复杂,所以我不是在寻找一个完整的解决方案,如果这甚至是可行的,我只是在寻找一些高级指南。

【问题讨论】:

  • 如果你有一个递归子类怎么办?像一个链表
  • 你必须有一种方法来消除递归,就像杰克逊一样。或者你根本不会在这种情况下使用它。

标签: java json gson


【解决方案1】:

我已经粗略地实现了支持这一点的反序列化器。它是完全通用的(与类型无关),但也昂贵且脆弱,我不会将它用于任何严重的事情。我发帖只是为了向其他人展示我所拥有的,如果他们最终需要做类似的事情。

public class UnwrappingDeserializer implements JsonDeserializer<Object> {

    //This Gson needs to be identical to the global one, sans this deserializer to prevent infinite recursion
    private Gson delegate;

    public UnwrappingDeserializer(Gson delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
        Object def = delegate.fromJson(json, type); //Gson doesn't care about unknown fields
        Class raw = GenericTypeReflector.erase(type);
        Set<Field> unwrappedFields = ClassUtils.getAnnotatedFields(raw, GsonUnwrap.class);
        for (Field field : unwrappedFields) {
            AnnotatedType fieldType = GenericTypeReflector.getExactFieldType(field, type);
            field.setAccessible(true);
            try {
                Object fieldValue = deserialize(json, fieldType.getType(), context);
                field.set(def, fieldValue);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        return def;
    }
}

然后可以通过new GsonBuilder().registerTypeHierarchyAdapter(Object.class, new UnwrappingDeserializer(new Gson())).create() 全局注册或通过registerTypeAdapter 注册特定类型。

注意事项:

  • 真正的实现应该递归检查整个类结构是否存在GsonUnwrap,将结果缓存在并发映射中,并且仅在需要时执行此过程。否则它应该立即返回def
  • 它还应该缓存发现的注释字段以避免每次都扫描层次结构
  • GenericTypeReflector 来自GeAnTyRef
  • ClassUtils#getAnnotatedFields 是我自己的实现,但它没有做任何特别的事情 - 它只是为类层次结构递归地收集声明的字段(通过 Class#getDeclaredFields
  • GsonUnwrap 只是一个简单的自定义注解

我认为序列化也可以做类似的事情。从Derlin's answer 链接的示例可以作为起点。

【讨论】:

  • 出色的工作,谢谢!一个小修复:我需要将行 Object fieldValue = deserialize(json, fieldType.getType(), context); 更改为 Object fieldValue = context.deserialize(json, fieldType.getType());
【解决方案2】:

目前,没有简单的方法可以做到这一点。无论如何,这里有一些使它起作用的指针/替代方法。


GsonFireGsonFire 实现了 Gson 缺少的一些有用功能。虽然它还没有提供自动包装/展开,但它可能是创建自定义逻辑的一个很好的起点。

如果您只需要序列化,您可以在Person 中为firstlast 添加getter,并使用@ExposeMethodResult 对其进行序列化。不幸的是,不支持设置器(参见Is possible to use setters when Gson deserializes a JSON?)。


另一种支持序列化的方法是遵循How to move fields to parent object 的建议。


自定义类型适配器:支持序列化和反序列化的唯一方法之一是创建自定义TypeAdapters。这不会是通用的,但它会适合您的用例。

Serialize Nested Object as Attributes 的帖子已经给大家举例了,这里不再赘述。

【讨论】:

猜你喜欢
  • 2010-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-26
  • 1970-01-01
  • 2014-03-30
相关资源
最近更新 更多