Gson 在反序列化对象时忽略 null
Posted
技术标签:
【中文标题】Gson 在反序列化对象时忽略 null【英文标题】:Gson ignore null when deserializing object 【发布时间】:2016-01-22 21:54:51 【问题描述】:我想反序列化一个在 Java 中包含空值的 json 字符串。我想将对象反序列化为 Properties
对象。 json 字符串类似于:
"prop1":null, "propr2":"fancy value"
当我使用反序列化时
String json = //
new Gson().fromJson(json, Properties.class);
由于Hastable
进入Properties
对象,我得到一个空指针异常。如何指示 Gson 忽略空值的反序列化?
【问题讨论】:
我遇到了同样的问题,即使使用自定义反序列化器也是如此。我可以并且确实检查空值,但检查很多。 api 中的任何内容可用于检查属性是否存在且值是否为非 null? 有时我认为提供属性的扩展会更简单。 @mat_boy 您介意将标题更改为“Gson 在反序列化 Properties 对象时忽略空值”吗?因为这似乎是您的问题所在。通常忽略null
值可能不容易或根本不可能。
【参考方案1】:
见http://sites.google.com/site/gson/gson-user-guide#TOC-Null-Object-Support:
Gson gson = new GsonBuilder().serializeNulls().create();
【讨论】:
这是为了序列化。我需要反序列化 我看到它是用于序列化的,但我想反序列化也可以。假设 gson 产生了可以再次反序列化的东西。【参考方案2】:我们有这个解决方案:
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()
【讨论】:
【参考方案3】:问题确实是Gson的默认适配器试图将null
放入Properties
,这是被禁止的。
要解决这个问题,您可以为Properties
编写自己的TypeAdapter
。然后,您必须使用 GsonBuilder
创建 Gson 实例,在该实例上 registered 该类型适配器。
下面展示了这样一个适配器的外观。它稍微严格一点,因为它会在序列化过程中阻止非字符串键和值(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 在反序列化对象时忽略 null的主要内容,如果未能解决你的问题,请参考以下文章
我可以让 XmlSerializer 在反序列化时忽略命名空间吗?
Spring数据redis在反序列化List类型时返回null