JsonSerializer不使用GSON为嵌套对象工作

时间:2017-03-13 20:04:47

标签: java json serialization gson android

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

{
    "user": {
        "id": 1234
        ... 
        "photos": [
            {
                "url": "http://....."
                ...
            },
            {
                "url": "http://....."
                ...
            }
        ]
    }
}

我想为userphotos编写自定义反序列化程序。

所以我有:

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 
    }  
}

并在初始化时执行此操作:

new GsonBuilder()
   .registerTypeAdapter(User.class, new User.Deserializer());
   .registerTypeAdapter(Photos.class, new Photos.Deserializer());

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

{
    "photos": [
         {
             "url": "http://....."
              ...
         },
         {
             "url": "http://....."
              ...
         },
         {
             "url": "http://....."
              ...
         }
]

它会正确点击Photo的反序列化程序

2 个答案:

答案 0 :(得分:1)

我认为这些类与json文件不匹配。在这个json文件中:

{
    "user": {
        "id": 1234
        ... 
        "photos": {
            "abc": {
                "url": "http://....."
                ...
            }
        }
    }
}

你有

public class User {
    private int id;
    private Photos photos;
} 

public class Photos {
   private MyUrl abc;
   private MyUrl bcd;
   private MyUrl cde;
   ...
}

public class MyUrl {
    private String url;
}

要获取照片的ArrayList,json应如下所示(注意方括号及其内容):

{
    "user": {
        "id": 1234
        ... 
        "photos": [
             { "url": "http://....." },
             { "url": "http://....." },
                ...
             { "url": "http://....." }
            ]
        }
    }
}

这最后一个杰森回应:

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

public class Photo{
    private String url;
}

答案 1 :(得分:1)

简而言之,有一个非正式的规则:一旦你声明一个某种类型的类型适配器(或原则上共享相同概念的(反)序列化器),那么你有自己管理其实例化及其子字段。因此,当您反序列化最顶层的User时,其idphotos会自行反序列化。请注意,Photo.Deserializer在您明确请求gson.fromJson(..., Photo.class)之后会被调用或隐式应用它(对于后者Gson默认使用内置策略,请参见ReflectiveTypeAdapterFactory)反序列化语境。如果你不绑定User,同样的原则会转到User.Deserializer,因此Gson使用ReflectiveTypeAdapterFactory.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)
            );
        }

    }

}

我假设代码中的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)
            );
        }

    }

}

如何使用:

final class Wrapper {

    final User user;

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

}
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));

输出:

  

1234
  HTTP:// .....
  http:// .....