我使用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并在该级别进行检测是不相关和优雅的(我想在这种情况下我必须事先欠下密钥列表存在其中我想检查)。以某种方式推断对象级别所需的信息是非常可取的。
我描述了布尔值示例的问题,但问题可能是一般化的,可能会引用所有基本类型。因此,对这个问题有一个通用的解决方案很棒。
答案 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有很大的影响,但是这可能比处理空值并将默认值存储在其他地方更为简洁。