如何实现@JsonUnwrap的Gson等价物

时间:2017-04-08 13:26:16

标签: java json gson

我知道Gson没有类似的功能,但有没有办法像public class Person { public int age; public Name name; } public class Name { public String first; public String last; } 一样添加对Json字段展开的支持?

目标是允许类似以下的结构:

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

被(de)序列化为:

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

而不是:

{{1}}

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

2 个答案:

答案 0 :(得分:2)

我做了一个支持这个的解串器的粗略实现。它是完全通用的(类型无关的),但也昂贵且易碎,我不会将它用于任何严重的事情。我发帖只是为了向别人展示我得到的东西,如果他们最终需要做类似的事情。

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链接的示例可以作为起点。

答案 1 :(得分:1)

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

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

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

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

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

线程Serialize Nested Object as Attributes已经为您提供了示例,因此我不会在此重复这些内容。