JsonSerializer 不适用于使用 GSON 的嵌套对象

Sre*_*ree 3 java serialization android json gson

我有一个 json 对象,如下所示:

{
    "user": {
        "id": 1234
        ... 
        "photos": [
            {
                "url": "http://....."
                ...
            },
            {
                "url": "http://....."
                ...
            }
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

user我想为和编写一个自定义解串器photos

所以我有:

public class User {
    private long id;
    private ArrayList<Photo> photos;
    ... 

    public static class Deserializer implements JsonDeserializer<User> {
        ... // does the custom serialization of the User object 
    }  
}

public class Photo {
    private String url;
    ... 

    public static class Deserializer implements JsonDeserializer<Photos> {
        ... // does the custom serialization of the Photo object 
    }  
}
Run Code Online (Sandbox Code Playgroud)

初始化时我这样做:

new GsonBuilder()
   .registerTypeAdapter(User.class, new User.Deserializer());
   .registerTypeAdapter(Photos.class, new Photos.Deserializer());
Run Code Online (Sandbox Code Playgroud)

但是,当我反序列化该类时User,它会命中 的User反序列化器,但永远不会命中 的Photo反序列化器。但是,如果我得到一个 json,其中照片对象未嵌套在用户 json 对象中,如下所示:

{
    "photos": [
         {
             "url": "http://....."
              ...
         },
         {
             "url": "http://....."
              ...
         },
         {
             "url": "http://....."
              ...
         }
]
Run Code Online (Sandbox Code Playgroud)

它将正确命中 的Photo解串器

Lyu*_*riv 5

简而言之,有一个非正式的规则:一旦您为某种类型声明了类型适配器(或原则上共享相同概念的(反)序列化器) ,那么您必须自己管理其实例化及其子字段。因此,当您反序列化最顶层的 时User,它的idphotos会自行反序列化。请注意,Photo.Deserializer一旦您通过反序列化上下文显式请求它gson.fromJson(..., Photo.class)或隐式应用它(对于后者 Gson 默认情况下使用内置策略,请参阅ReflectiveTypeAdapterFactory示例),就会调用它。User如果你不绑定,同样的原理也适用User.Deserializer,因此 GsonReflectiveTypeAdapterFactory.Adapter<T>使用反射来迭代所有字段本身。更短的是:Gson 不会合并多个策略(至少在默认情况下),因此您要么将对象构造和设置委托给 Gson,要么完全实例化它。

知道了,User.Deserializer可以按如下方式实现:

final class User {

    final long id;
    final List<Photo> photos;

    private User(final long id, final List<Photo> photos) {
        this.id = id;
        this.photos = photos;
    }

    static final class Deserializer
            implements JsonDeserializer<User> {

        private static final Type photoListType = new TypeToken<List<Photo>>() {
        }.getType();

        @Override
        public User deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) {
            // Note that you must pick up properties first
            final JsonObject jsonObject = jsonElement.getAsJsonObject();
            return new User(
                    // And then delegate them to the deserialization context specifying the target type
                    context.deserialize(jsonObject.get("id"), long.class),
                    // You can deconstruct JsonElement recursively, but deserialization context respects Gson context built with GsonBuilder
                    // This also does trigger the Photo.Deserializer
                    context.deserialize(jsonObject.get("photos"), photoListType)
            );
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

我假设Photos你的代码中有一个拼写错误,它应该是Photo. 如果不是,则可以为 实施类似的解决方案Photos

final class Photo {

    final String url;

    private Photo(final String url) {
        this.url = url;
    }

    static final class Deserializer
            implements JsonDeserializer<Photo> {

        @Override
        public Photo deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) {
            final JsonObject jsonObject = jsonElement.getAsJsonObject();
            return new Photo(
                    // jsonObject.get("url").getAsString() can be more simple, but it does not respect Gson instance configuration
                    context.deserialize(jsonObject.get("url"), String.class)
            );
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

如何使用:

final class Wrapper {

    final User user;

    private Wrapper(final User user) {
        this.user = user;
    }

}
Run Code Online (Sandbox Code Playgroud)
final Gson gson = new GsonBuilder()
        .registerTypeAdapter(User.class, new User.Deserializer())
        .registerTypeAdapter(Photo.class, new Photo.Deserializer())
        .create();
final Wrapper wrapper = gson.fromJson(JSON, Wrapper.class);
System.out.println(wrapper.user.id);
wrapper.user.photos.forEach(p -> System.out.println(p.url));
Run Code Online (Sandbox Code Playgroud)

输出:

第 1234