【问题标题】:Gson ignore null when deserializing objectGson 在反序列化对象时忽略 null
【发布时间】: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 值可能不容易或根本不可能。

标签: java json gson deserialization


【解决方案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));
    }
    

    【讨论】:

      猜你喜欢
      • 2016-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      相关资源
      最近更新 更多