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));
}
3条答案
按热度按时间cgh8pdjw1#
问题确实是Gson的默认适配器试图将
null
放入Properties
,这是被禁止的。要解决这个问题,可以为
Properties
编写自己的TypeAdapter
,然后必须使用注册了该类型适配器的GsonBuilder
创建Gson示例。下面显示了这样一个适配器的外观。它稍微严格一点,因为它在序列化过程中阻止非String键和值(Gson的默认适配器不这样做),因为它们会在反序列化过程中导致问题。但是,您可以使用Gson.getDelegateAdapter替换它,并将序列化委托给Gson的适配器。
s4n0splo2#
我们有这样的解决方案:
1.您的所有数据类都需要扩展抽象类
2.创建此安全反序列化程序以从JSON中删除空值
3.并将其注册到GSON示例中
kyks70gy3#
请访问http://sites.google.com/site/gson/gson-user-guide#TOC-Null-Object-Support:
Gson gson = new GsonBuilder().serializeNulls().create();