当前位置: 首页 > 知识库问答 >
问题:

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

尉迟栋
2023-03-14

我正在尝试为我正在构建的Java(JAX-RS)Web服务实现JSON合并补丁。

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

鉴于此记录

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

,以下JSON更新文档

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

应该为"a"设置一个新值并删除"c"中的"f"

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

做什么?

共有3个答案

易修洁
2023-03-14

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

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

您还可以使用字符串类型来检测丢失的值,但还需要将字符串解析为所需的类型。

左丘嘉木
2023-03-14

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

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

郭恩
2023-03-14

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

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

然而,通过使用Java 8的Optionals对数据结构建模,我可以做到这一点:区分未设置的内容和设置为null的内容。以下是我的结论:

1) 我将html" target="_blank">数据对象中的所有字段替换为可选

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) 基于这个有用的SO答案实现了序列化器和反序列化器。

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和empty可选都被序列化为null(或简单地从输出中删除),同时将JSON读取到可选字段中,这样,如果该字段是null,我知道JSON输入中缺少该字段,如果该字段是可选的。空我知道它在输入中被设置为空。

例子:

System.out.println(gsonOptFact.toJson(new BasicObjectOptional(true)));
// {"someKey":"someValue","someNumber":42,"mayBeNull":null}

System.out.println(gsonOptFact.toJson(new ComplexObjectOptional(true)));
// {"theTitle":"Complex Object","stringArray":["Hello","World"],"theObject":{"someKey":"someValue","someNumber":42,"mayBeNull":null}}

// Now read back in:
String basic = "{\"someKey\":\"someValue\",\"someNumber\":42,\"mayBeNull\":null}";
String complex = "{\"theTitle\":\"Complex Object\",\"stringArray\":[\"Hello\",\"world\"],\"theObject\":{\"someKey\":\"someValue\",\"someNumber\":42,\"mayBeNull\":null}}";
String complexMissing = "{\"theTitle\":\"Complex Object\",\"theObject\":{\"someKey\":\"someValue\",\"mayBeNull\":null}}";

BasicObjectOptional boo = gsonOptFact.fromJson(basic, BasicObjectOptional.class);
System.out.println(boo);
// someKey = Optional[someValue], someNumber = Optional[42], mayBeNull = Optional.empty

ComplexObjectOptional coo = gsonOptFact.fromJson(complex, ComplexObjectOptional.class);
System.out.println(coo);
// theTitle = Optional[Complex Object], stringArray = Optional[[Optional[Hello], Optional[world]]], theObject = (Optional[someKey = Optional[someValue], someNumber = Optional[42], mayBeNull = Optional.empty])

ComplexObjectOptional coom = gsonOptFact.fromJson(complexMissing, ComplexObjectOptional.class);
System.out.println(coom);
// theTitle = Optional[Complex Object], stringArray = null, theObject = (Optional[someKey = Optional[someValue], someNumber = null, mayBeNull = Optional.empty])

我认为这将允许我将JSON合并补丁与现有数据对象很好地集成。

 类似资料:
  • 首先,我想说这是一个大学项目。 我有三节课<代码>订单抽象类和从订单继承的和类。 我使用Gson序列化/反序列化子类,但我遇到了一点问题。订单类有一个字段,gson使用该字段来确定订单类型,DineIn或Delivery。 序列化工作得很好。问题是,每当我尝试反序列化时,类型字段值都不会读取,并且总是设置为null,即使它存在于JSON文件中。当有很多字段时,就会发生这种情况,因为当我尝试在较小的

  • 我有一个Java对象,我想序列化为JSON。但是,在此之前,我为该对象的字段设置了一些属性。现在我想将这个对象序列化为JSON。如何仅序列化显式赋值的字段,而基本上排除所有其他字段? 对我来说,在对象类上添加这些注释是不起作用的:和。原因是我有一些基本数据类型的字段。通过添加上述两个注释之一,我并没有阻止原始数据类型字段被序列化。例如,我有几个布尔字段,它们将被序列化为默认值。但是,我不希望这些字

  • 我想序列化特定字段或类的空值。 在GSON中,选项适用于整个JSON。 例子: 使用GSON创建JSON: 电流输出: 期望输出: 如何达到预期的输出? 首选解决方案应具有以下特性: 默认情况下不要序列化空值 为具有特定注释的字段序列化空值。

  • 我试图使用Gson自定义序列化将以下类序列化为Json。

  • 我正在尝试为接受JSON输入的前端库创建一个java SDK。本质上,此SDK将特定类型的对象转换为JSON,然后由前端库使用。 我正在使用杰克逊的多态序列化/反序列化使用它的注释系统。 我有一个基类a和扩展a的两个子类B和C。类a有一个类型字段,我用它来决定要使用哪个类(B或C)。语法如下所示: 现在,当我使用Jackson的ObjectMapper函数读取字符串化的JSON并转换为类A时,我根

  • 我正在努力用本地化反序列化世界天气在线API结果。 是否有更好、更简单的解决方案,只为lang对象编写一个自定义反序列化器,并允许GSON自动反序列化其余数据?