Gson serialize null for specific class or field

后端 未结 5 419
面向向阳花
面向向阳花 2021-01-01 11:00

I want to serialize nulls for a specific field or class.

In GSON, the option serializeNulls() applies to the whole JSON.

Example:



        
5条回答
  •  时光说笑
    2021-01-01 11:48

    For those looking for a Java version of @Joris's excellent answer, the below code should do the trick. It's largely just a translation of the Kotlin, with a minor improvement to how the serialized name of the attribute is fetched to ensure it always works when the serialized name is different than the attribute name (see the comments on the original answer).

    This is the TypeAdapterFactory implementation:

    public class NullableAdapterFactory implements TypeAdapterFactory {
        @Override
        public  TypeAdapter create(Gson gson, TypeToken type) {
            Field[] declaredFields = type.getRawType().getDeclaredFields();
            List nullableFieldNames = new ArrayList<>();
            List nonNullableFieldNames = new ArrayList<>();
    
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(JsonNullable.class)) {
                    if (declaredField.getAnnotation(SerializedName.class) != null) {
                        nullableFieldNames.add(declaredField.getAnnotation(SerializedName.class).value());
                    } else {
                        nullableFieldNames.add(declaredField.getName());
                    }
                } else {
                    if (declaredField.getAnnotation(SerializedName.class) != null) {
                        nonNullableFieldNames.add(declaredField.getAnnotation(SerializedName.class).value());
                    } else {
                        nonNullableFieldNames.add(declaredField.getName());
                    }
                }
            }
    
            if (nullableFieldNames.size() == 0) {
                return null;
            }
    
            TypeAdapter delegateAdapter = gson.getDelegateAdapter(NullableAdapterFactory.this, type);
            TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class);
    
            return new TypeAdapter() {
                @Override
                public void write(JsonWriter out, T value) throws IOException {
                    JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
                    for (String name: nonNullableFieldNames) {
                        if (jsonObject.has(name) && jsonObject.get(name) instanceof JsonNull) {
                            jsonObject.remove(name);
                        }
                    }
    
                    out.setSerializeNulls(true);
                    elementAdapter.write(out, jsonObject);
                }
    
                @Override
                public T read(JsonReader in) throws IOException {
                    return delegateAdapter.read(in);
                }
    
            };
        }
    }
    

    And this is the @JsonNullable annotation to mark the target attributes:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface JsonNullable {
    }
    

    I implemented it as an @JsonAdapter(NullableAdapterFactory.class) annotation on the object class, rather registering it as a TypeAdapterFactory on the GsonBuilder instance, so my object classes looked a bit like this:

    @JsonAdapter(NullableAdapterFactory.class)
    public class Person {
      public String firstName;
      public String lastName;
    
      @JsonNullable
      public String someNullableInfo;
    }
    
    

    However, the other approach should work just as well with this code if preferred.

提交回复
热议问题