Gson ignore null when deserializing object

前端 未结 3 1521
清歌不尽
清歌不尽 2021-01-02 08:13

I want to deserialize a json string that containts a null value in Java. I want to deserialize the object to a Properties object. The json string is something l

相关标签:
3条回答
  • 2021-01-02 08:19

    The problem is indeed that Gson's default adapter tries to put null into the Properties, which is forbidden.

    To solve this, you could write your own TypeAdapter for Properties. You would then have to create Gson instances using a GsonBuilder on which you registered that type adapter.

    The following shows how such an adapter could look. It is slightly more strict in that it prevents non-String keys and values during serialization (which Gson's default adapter does not) since they would cause issues during deserialization. You could however replace that and delegate serialization to Gson's adapter by using Gson.getDelegateAdapter​.

    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 讨论(0)
  • 2021-01-02 08:23

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

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

    0 讨论(0)
  • 2021-01-02 08:27

    We have this solution:

    1. All of your data classes need to extends abstract class

    abstract class PoJoClass
    

    2. Create this safe deserializer to delete nulls from 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. And register it in your GSON instance

    val gson = Gson().newBuilder()
                    .registerTypeHierarchyAdapter(PoJoClass::class.java, SafeDeserializer<PoJoClass>(Gson()))
                    .create()
    
    0 讨论(0)
提交回复
热议问题