Gson反序列化:如何区分缺少的字段和显式设置为null的字段?

时间:2015-11-16 14:04:32

标签: java json serialization gson

我正在尝试为我建立的Java(JAX-RS)网络服务实施JSON Merge Patch

要点是记录的部分更新是通过向服务器发送一个只包含应该更改的字段的JSON文档来完成的。

鉴于此记录

{
  "a": "b",
  "c": {
    "d": "e",
    "f": "g"
  }
}

,以下JSON更新文档

{
  "a":"z",
  "c": {
    "f": null
  }
}

应为"a"设置新值,并在"f"内删除"c"

后者是问题所在。我不知道如何区分f缺失的输入和f为null的输入。据我所知,两者都会被反序列化为null。目标Java对象。

做什么?

3 个答案:

答案 0 :(得分:4)

我承认mlk的答案,但鉴于我已经(并且仍然需要)JSON对象的POJO表示,我觉得自动映射仍然比手动查找更好。

这样做的挑战在于,正如我所说,gson.fromJson(...)将填充的相应POJO 中将缺失值和显式空值都设置为null。 (与R' NULLNA不同,Java只有一个表示"不存在"。)

但是,通过使用Java 8's Optionals建模我的数据结构,我可以做到这一点:区分未设置的内容和设置为null的内容。这就是我最终得到的结果:

1)我用Optional<T>替换了数据对象中的所有字段。

public class BasicObjectOptional {

    private Optional<String> someKey;
    private Optional<Integer> someNumber;
    private Optional<String> mayBeNull;

    public BasicObjectOptional() {
    }

    public BasicObjectOptional(boolean initialize) {
        if (initialize) {
            someKey = Optional.ofNullable("someValue");
            someNumber = Optional.ofNullable(42);
            mayBeNull = Optional.ofNullable(null);
        }
    }

    @Override
    public String toString() {
        return String.format("someKey = %s, someNumber = %s, mayBeNull = %s",
                                            someKey, someNumber, mayBeNull);
    }

}

或嵌套的:

public class ComplexObjectOptional {

    Optional<String> theTitle;  
    Optional<List<Optional<String>>> stringArray;
    Optional<BasicObjectOptional> theObject;

    public ComplexObjectOptional() {
    }

    public ComplexObjectOptional(boolean initialize) {
        if (initialize) {
            theTitle = Optional.ofNullable("Complex Object");   
            stringArray =    Optional.ofNullable(Arrays.asList(Optional.ofNullable("Hello"),Optional.ofNullable("World")));
            theObject = Optional.ofNullable(new BasicObjectOptional(true));
        }
    }

    @Override
    public String toString() {
        return String.format("theTitle = %s, stringArray = %s, theObject = (%s)", theTitle, stringArray, theObject);
    }   
}

2)实现了序列化器和反序列化器based on this useful SO answer

public class OptionalTypeAdapter<E> extends TypeAdapter<Optional<E>> {

    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {

        //@Override
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            Class<T> rawType = (Class<T>) type.getRawType();
            if (rawType != Optional.class) {
                return null;
            }
            final ParameterizedType parameterizedType = (ParameterizedType) type.getType();
            final Type actualType = parameterizedType.getActualTypeArguments()[0];
            final TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(actualType));
            return new OptionalTypeAdapter(adapter);
        }
    };
    private final TypeAdapter<E> adapter;

    public OptionalTypeAdapter(TypeAdapter<E> adapter) {
        this.adapter = adapter;
    }

    @Override
    public void write(JsonWriter out, Optional<E> value) throws IOException {
        if(value == null || !value.isPresent()){
            out.nullValue();
        } else {
            adapter.write(out, value.get());
        }
    }

    @Override
    public Optional<E> read(JsonReader in) throws IOException {
        final JsonToken peek = in.peek();
        if(peek != JsonToken.NULL){
            return Optional.ofNullable(adapter.read(in));
        }
        in.nextNull();
        return Optional.empty();
    }

}

3)初始化Gson时注册了此适配器。

Gson gsonOptFact = new GsonBuilder()
    .serializeNulls() // matter of taste, just for output anyway
    .registerTypeAdapterFactory(OptionalTypeAdapter.FACTORY)
    .create();

这允许我编写JSON,使null和空Optional被序列化为null(或者只是从输出中删除),同时将JSON读入{ {1}}字段如果字段为Optional我知道JSON输入中缺少该字段,如果字段为null我知道它已设置为Optional.empty在输入中。

示例:

null

我认为这将允许我很好地将JSON Merge Patch与我现有的数据对象集成。

答案 1 :(得分:0)

我认为您必须使用JsonObject并查看返回的Json对象。

您可以使用JsonParser.parse(java.io.Reader)来获取JsonObject。

答案 2 :(得分:0)

对于基本类型,请使用可为空的版本。

例如,在通常使用long的地方使用Long。整数替换为int等。

您还可以在要检测缺失值的地方使用String类型,但随后还需要将String解析为所需的类型。