I want to serialize nulls for a specific field or class.
In GSON, the option serializeNulls()
applies to the whole JSON.
Example:
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.