Gson 反序列化对象时忽略 null

问题描述 投票:0回答:4

我想反序列化Java中包含空值的json字符串。我想将对象反序列化为

Properties
对象。 json 字符串类似于:

{"prop1":null, "propr2":"fancy value"}

当我使用反序列化时

String json = //
new Gson().fromJson(json, Properties.class);

由于

Hastable
进入了
Properties
对象,因此出现空指针异常。如何指示 Gson 忽略空值的反序列化?

java json gson deserialization
4个回答
0
投票

问题确实是 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));
}

0
投票

如果您使用 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);
    }
}

在此代码中:

  • 我们创建一个自定义反序列化器 PropertiesDeserializer 实现 JsonDeserializer。
  • 在反序列化方法中,我们迭代 JSON 对象的键 并将键值对添加到 Properties 对象。
  • 如果值为null,我们在Properties中显式将其设置为null 对象。
  • 我们使用 Gson 注册这个自定义解串器 Gson 设置期间的 registerTypeAdapter 方法。

使用此自定义反序列化器,Gson 将在 Properties 对象的反序列化期间包含空值。


-1
投票

我们有这个解决方案:

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()

-7
投票

请参阅 http://sites.google.com/site/gson/gson-user-guide#TOC-Null-Object-Support

Gson gson = new GsonBuilder().serializeNulls().create();

© www.soinside.com 2019 - 2024. All rights reserved.