找出Gson SerializedName注释的类字段是否实际存在于Json

时间:2015-10-23 19:54:46

标签: java json parsing serialization gson

我使用google Gson解析json并创建一个合适的对象:

public class Settings {
    @SerializedName("version")
    public String version = "1";

    @SerializedName("ad_native_enabled")
    public boolean nativeAdEnabled = false;
}

问题是我需要知道nativeAdEnabled的值是否实际上是从json解析的,或者它是我指定的默认值,即json中是否存在ad_native_enabled键, 或不?我尝试使用布尔而不是布尔,只做空检查,但Gson反序列化失败了。这是我的json的片段:

{
    "status": "success",
    "ad_native_enabled": false,
}

声明

在我的情况下,用手解析json并在该级别进行检测是不相关和优雅的(我想在这种情况下我必须事先欠下密钥列表存在其中我想检查)。以某种方式推断对象级别所需的信息是非常可取的。

我描述了布尔值示例的问题,但问题可能是一般化的,可能会引用所有基本类型。因此,对这个问题有一个通用的解决方案很棒。

1 个答案:

答案 0 :(得分:2)

我知道你说过你已经尝试过了,但使用Boolean字段应该可行。我已经减少了你的例子,它按预期工作。

我像这样定义了Settings类:

public static class Settings {
    @SerializedName("ad_native_enabled")
    public Boolean nativeAdEnabled;
}

如果您随后解析包含字段的JSON:

String json = "{\"ad_native_enabled\": false}";
Settings settings = gson.fromJson(json, Settings.class);
System.out.println(settings.nativeAdEnabled);  // prints false

而如果您解析不包含该字段的JSON:

String emptyJson = "{}";
Settings emptySettings = gson.fromJson(emptyJson, Settings.class);
System.out.println(emptySettings.nativeAdEnabled);  // prints null

您是否可以将字段的默认值保留为false?如果是这样,第二个示例也将打印false。此外,似乎GSON在JSON对象中的最后一个属性之后并不特别喜欢尾随逗号 - 也许这就是为什么你会收到错误?

在看到你的评论之后,我想到了更多关于是否有可能以某种方式支持默认值,同时仍然能够判断该字段是否存在于JSON中。我能想到的最好的解决方案是引入一个新的包装器类型,带有一个自定义的反序列化器。

我首先定义了这个包装类型,它只包含字段的实际值,以及该值是否为默认值的指示符:

public static class ValueWrapper<T> {
    public final T value;
    public final boolean isDefaultValue;

    public ValueWrapper(T value, boolean isDefaultValue) {
        this.value = value;
        this.isDefaultValue = isDefaultValue;
    }
}

Settings类看起来像这样:

public static class Settings {
    @SerializedName("ad_native_enabled")
    public ValueWrapper<Boolean> nativeAdEnabled = new ValueWrapper<>(false, true);
}

此处我默认将字段定义为值false,这就是isDefaultValue设置为true的原因。

然后我为这种类型定义了一个自定义反序列化器。基本思想是采用您尝试反序列化的ValueWrapper类型,提取其泛型参数,将JSON中的实际值反序列化为通用参数类型,然后返回一个新的ValueWrapper其中{ {1}}设置为isDefaultValue。这个解串器看起来像这样:

false

现在我们需要做的就是注册自定义反序列化器:

public static class ValueWrapperDeserializer implements JsonDeserializer<ValueWrapper<?>> {
    @Override
    public ValueWrapper<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        ParameterizedType parameterizedType = (ParameterizedType) typeOfT;
        Type valueType = parameterizedType.getActualTypeArguments()[0];
        Object value = context.deserialize(json, valueType);
        return new ValueWrapper<>(value, false);
    }
}

然后我们可以完成上面的两个例子:

    Gson gson = new GsonBuilder()
            .registerTypeAdapter(ValueWrapper.class, new ValueWrapperDeserializer())
            .create();

因此,这允许我们使用默认值,但仍然可以使用String json = "{\"ad_native_enabled\": false}"; Settings settings = gson.fromJson(json, Settings.class); System.out.println(settings.nativeAdEnabled.value); // prints false System.out.println(settings.nativeAdEnabled.isDefaultValue); // prints false String emptyJson = "{}"; Settings emptySettings = gson.fromJson(emptyJson, Settings.class); System.out.println(emptySettings.nativeAdEnabled.value); // prints false System.out.println(emptySettings.nativeAdEnabled.isDefaultValue); //prints true 来判断字段是否已设置。这显然对isDefaultValue对象的用户的API有很大的影响,但是这可能比处理空值并将默认值存储在其他地方更为简洁。