我想反序列化Java中包含空值的json字符串。我想将对象反序列化为
Properties
对象。 json 字符串类似于:
{"prop1":null, "propr2":"fancy value"}
当我使用反序列化时
String json = //
new Gson().fromJson(json, Properties.class);
由于
Hastable
进入了 Properties
对象,因此出现空指针异常。如何指示 Gson 忽略空值的反序列化?
问题确实是 Gson 的默认适配器试图将
null
放入 Properties
中,这是被禁止的。
要解决这个问题,您可以为 TypeAdapter
编写自己的
Properties
。然后,您必须使用您在其上注册该类型适配器的
GsonBuilder
创建Gson实例。
下图显示了此类适配器的外观。它稍微严格一些,因为它会在序列化期间阻止非 String 键和值(Gson 的默认适配器不会),因为它们会在反序列化期间导致问题。但是,您可以使用 Gson.getDelegateAdapter.
替换它并将序列化委托给 Gson 的适配器private static final TypeAdapter<Properties> PROPERTIES_ADAPTER = new TypeAdapter<Properties>() {
@Override
public Properties read(JsonReader in) throws IOException {
in.beginObject();
Properties properties = new Properties();
while (in.hasNext()) {
String name = in.nextName();
JsonToken peeked = in.peek();
// Ignore null values
if (peeked == JsonToken.NULL) {
in.nextNull();
continue;
}
// Allow Json boolean
else if (peeked == JsonToken.BOOLEAN) {
properties.setProperty(name, Boolean.toString(in.nextBoolean()));
}
// Expect string or number
else {
properties.setProperty(name, in.nextString());
}
}
in.endObject();
return properties;
}
private String asString(Object obj) {
if (obj.getClass() != String.class) {
throw new IllegalArgumentException("Properties contains non-String object " + obj);
}
return (String) obj;
}
/*
* Could also delegate to Gson's implementation for serialization.
* However, that would not fail if the Properties contains non-String values,
* which would then cause issues when deserializing the Json again.
*/
@Override
public void write(JsonWriter out, Properties properties) throws IOException {
out.beginObject();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
// Make sure that key is a String, otherwise properties
// cannot be deserialized again
out.name(asString(entry.getKey()));
Object value = entry.getValue();
// Be lenient and allow Numbers and Booleans as values
if (value instanceof Number) {
out.value((Number) value);
} else if (value instanceof Boolean) {
out.value((Boolean) value);
} else {
// Require that value is a String
out.value(asString(value));
}
}
out.endObject();
}
}.nullSafe(); // Handle null Properties, e.g. `Properties props = null`
public static void main(String[] args) throws IOException {
Gson gson = new GsonBuilder()
// Register the custom type adapter
.registerTypeAdapter(Properties.class, PROPERTIES_ADAPTER)
.create();
String json = "{\"prop1\":true, \"prop2\":\"text\", \"prop3\":null}";
Properties deserialized = gson.fromJson(json, Properties.class);
System.out.println("Deserialized: " + deserialized);
Properties properties = new Properties();
properties.setProperty("prop", "text");
// Discouraged to put non-Strings, but type adapter supports these
properties.put("boolean", true);
properties.put("number", 1234);
System.out.println("Serialized: " + gson.toJson(properties));
}
如果您使用 Gson 反序列化 Properties 对象并希望在反序列化期间包含 null 值,则需要实现自定义反序列化器。默认情况下,Gson 不支持 Properties 对象中的 null 值,因为 Properties 扩展了 Hashtable,并且 Gson 的默认行为是排除 null 值。
以下是如何为 Properties 对象实现自定义反序列化器:
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.Properties;
import java.util.Set;
public class PropertiesDeserializer implements JsonDeserializer<Properties> {
@Override
public Properties deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Properties properties = new Properties();
// Convert JSON object to Java Map
JsonObject jsonObject = json.getAsJsonObject();
Set<String> keys = jsonObject.keySet();
for (String key : keys) {
JsonElement value = jsonObject.get(key);
// Add null values as null strings to Properties object
if (value.isJsonNull()) {
properties.setProperty(key, null);
} else {
properties.setProperty(key, value.getAsString());
}
}
return properties;
}
public static void main(String[] args) {
String json = "{\"key1\":\"value1\",\"key2\":null,\"key3\":\"value3\"}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Properties.class, new PropertiesDeserializer());
Gson gson = gsonBuilder.create();
Properties properties = gson.fromJson(json, Properties.class);
System.out.println(properties);
}
}
在此代码中:
使用此自定义反序列化器,Gson 将在 Properties 对象的反序列化期间包含空值。
我们有这个解决方案:
1。所有数据类都需要扩展抽象类
abstract class PoJoClass
2。创建这个安全的反序列化器以从 JSON 中删除空值
class SafeDeserializer<T : PoJoClass>(private val gson: Gson) :JsonDeserializer<T> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): T {
val jsonObject = json as JsonObject
removeNullsFromJson(jsonObject)
return gson.fromJson(jsonObject, typeOfT)
}
private fun removeNullsFromJson(jsonObject: JsonObject) {
val iterator = jsonObject.keySet().iterator()
while (iterator.hasNext()) {
val key = iterator.next()
when(val json = jsonObject[key]){
is JsonObject -> removeNullsFromJson(json)
is JsonNull -> iterator.remove()
}
}
}
}
3.并将其注册到您的 GSON 实例中
val gson = Gson().newBuilder()
.registerTypeHierarchyAdapter(PoJoClass::class.java, SafeDeserializer<PoJoClass>(Gson()))
.create()
请参阅 http://sites.google.com/site/gson/gson-user-guide#TOC-Null-Object-Support:
Gson gson = new GsonBuilder().serializeNulls().create();