[英]Gson serialize null for specific class or field
I want to serialize nulls for a specific field or class.我想序列化特定字段或类的空值。
In GSON, the option serializeNulls()
applies to the whole JSON.在 GSON 中,选项serializeNulls()
适用于整个 JSON。
Example:例子:
class MainClass {
public String id;
public String name;
public Test test;
}
class Test {
public String name;
public String value;
}
MainClass mainClass = new MainClass();
mainClass.id = "101"
// mainClass has no name.
Test test = new Test();
test.name = "testName";
test.value = null;
mainClass.test = test;
Creating JSON using GSON:使用 GSON 创建 JSON:
GsonBuilder builder = new GsonBuilder().serializeNulls();
Gson gson = builder.create();
System.out.println(gson.toJson(mainClass));
Current ouput:当前输出:
{
"id": "101",
"name": null,
"test": {
"name": "testName",
"value": null
}
}
Desired output:期望的输出:
{
"id": "101",
"test": {
"name": "testName",
"value": null
}
}
How to achieve the desired output?如何达到预期的输出?
Preferred solution would have the following properties:首选解决方案将具有以下属性:
I have a solution similar to the one of Aleksey but that can be applied to one or more fields in any class (example in Kotlin):我有一个类似于 Aleksey 的解决方案,但它可以应用于任何类中的一个或多个字段(Kotlin 中的示例):
Create a new annotation for fields that should be serialized as null:为应序列化为 null 的字段创建新注释:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class SerializeNull
Create a TypeAdapterFactory
that checks if a class has fields annotated with this annotation and removes the fields that are null
and not annotated with the annotation from the JsonTree
when writing the object:创建一个TypeAdapterFactory
来检查一个类是否具有使用此注解注解的字段,并在编写对象时从JsonTree
中删除为null
且未使用该注解注解的字段:
class SerializableAsNullConverter : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
fun Field.serializedName() = declaredAnnotations
.filterIsInstance<SerializedName>()
.firstOrNull()?.value ?: name
val declaredFields = type.rawType.declaredFields
val nullableFieldNames = declaredFields
.filter { it.declaredAnnotations.filterIsInstance<SerializeNull>().isNotEmpty() }
.map { it.serializedName() }
val nonNullableFields = declaredFields.map { it.serializedName() } - nullableFieldNames
return if (nullableFieldNames.isEmpty()) {
null
} else object : TypeAdapter<T>() {
private val delegateAdapter = gson.getDelegateAdapter(this@SerializableAsNullConverter, type)
private val elementAdapter = gson.getAdapter(JsonElement::class.java)
override fun write(writer: JsonWriter, value: T?) {
val jsonObject = delegateAdapter.toJsonTree(value).asJsonObject
nonNullableFields
.filter { jsonObject.get(it) is JsonNull }
.forEach { jsonObject.remove(it) }
val originalSerializeNulls = writer.serializeNulls
writer.serializeNulls = true
elementAdapter.write(writer, jsonObject)
writer.serializeNulls = originalSerializeNulls
}
override fun read(reader: JsonReader): T {
return delegateAdapter.read(reader)
}
}
}
}
Register the adapter with your Gson instance:向您的 Gson 实例注册适配器:
val builder = GsonBuilder().registerTypeAdapterFactory(SerializableAsNullConverter())
And annotate the fields you would like to be nullable:并注释您希望可以为空的字段:
class MyClass(val id: String?, @SerializeNull val name: String?)
Serialization result:序列化结果:
val myClass = MyClass(null, null)
val gson = builder.create()
val json = gson.toJson(myClass)
json: json:
{
"name": null
}
I have interface to check when object should be serialized as null:我有接口来检查对象何时应序列化为空:
public interface JsonNullable {
boolean isJsonNull();
}
And the corresponding TypeAdapter (supports write only)以及对应的TypeAdapter(支持只写)
public class JsonNullableAdapter extends TypeAdapter<JsonNullable> {
final TypeAdapter<JsonElement> elementAdapter = new Gson().getAdapter(JsonElement.class);
final TypeAdapter<Object> objectAdapter = new Gson().getAdapter(Object.class);
@Override
public void write(JsonWriter out, JsonNullable value) throws IOException {
if (value == null || value.isJsonNull()) {
//if the writer was not allowed to write null values
//do it only for this field
if (!out.getSerializeNulls()) {
out.setSerializeNulls(true);
out.nullValue();
out.setSerializeNulls(false);
} else {
out.nullValue();
}
} else {
JsonElement tree = objectAdapter.toJsonTree(value);
elementAdapter.write(out, tree);
}
}
@Override
public JsonNullable read(JsonReader in) throws IOException {
return null;
}
}
Use it as follows:按如下方式使用它:
public class Foo implements JsonNullable {
@Override
public boolean isJsonNull() {
// You decide
}
}
In the class where Foo value should be serialized as null.在 Foo 值应序列化为 null 的类中。 Note that foo value itself must be not null, otherwise custom adapter annotation will be ignored.注意 foo 值本身不能为空,否则自定义适配器注解将被忽略。
public class Bar {
@JsonAdapter(JsonNullableAdapter.class)
public Foo foo = new Foo();
}
For those looking for a Java version of @Joris's excellent answer, the below code should do the trick.对于那些寻找 @Joris 出色答案的 Java 版本的人来说,下面的代码应该可以解决问题。 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).它在很大程度上只是 Kotlin 的翻译,对如何获取属性的序列化名称进行了微小的改进,以确保它在序列化名称与属性名称不同时始终有效(请参阅原始答案的注释)。
This is the TypeAdapterFactory
implementation:这是TypeAdapterFactory
的实现:
public class NullableAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Field[] declaredFields = type.getRawType().getDeclaredFields();
List<String> nullableFieldNames = new ArrayList<>();
List<String> 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<T> delegateAdapter = gson.getDelegateAdapter(NullableAdapterFactory.this, type);
TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@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);
}
}
boolean originalSerializeNulls = out.getSerializeNulls();
out.setSerializeNulls(true);
elementAdapter.write(out, jsonObject);
out.setSerializeNulls(originalSerializeNulls);
}
@Override
public T read(JsonReader in) throws IOException {
return delegateAdapter.read(in);
}
};
}
}
And this is the @JsonNullable
annotation to mark the target attributes:这是标记目标属性的@JsonNullable
注解:
@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)
注释,而不是将其注册为TypeAdapterFactory
实例上的GsonBuilder
,所以我的对象类看起来有点像这样:
@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.但是,如果愿意,另一种方法应该同样适用于此代码。
Create subclass of com.google.gson.TypeAdapter
and register it for required field using annotation com.google.gson.annotations.JsonAdapter
.创建com.google.gson.TypeAdapter
的子类并使用注释com.google.gson.annotations.JsonAdapter
将其注册为必填字段。 Or register it using GsonBuilder.registerTypeAdapter
.或者使用GsonBuilder.registerTypeAdapter
注册它。 In that adapter write
(and read
) should be implemented.在那个适配器中write
(和read
)应该被实现。 For example:例如:
public class JsonTestNullableAdapter extends TypeAdapter<Test> {
@Override
public void write(JsonWriter out, Test value) throws IOException {
out.beginObject();
out.name("name");
out.value(value.name);
out.name("value");
if (value.value == null) {
out.setSerializeNulls(true);
out.nullValue();
out.setSerializeNulls(false);
} else {
out.value(value.value);
}
out.endObject();
}
@Override
public Test read(JsonReader in) throws IOException {
in.beginObject();
Test result = new Test();
in.nextName();
if (in.peek() != NULL) {
result.name = in.nextString();
} else {
in.nextNull();
}
in.nextName();
if (in.peek() != NULL) {
result.value = in.nextString();
} else {
in.nextNull();
}
in.endObject();
return result;
}
}
in MainClass
add JsonAdapter
annotation with the adapter to Test
class field:在MainClass
中将带有适配器的JsonAdapter
注释添加到Test
类字段:
public static class MClass {
public String id;
public String name;
@JsonAdapter(JsonTestNullableAdapter.class)
public Test test;
}
the output of System.out.println(new Gson.toJson(mainClass))
is: System.out.println(new Gson.toJson(mainClass))
的输出是:
{
"id": "101",
"test": {
"name": "testName",
"value": null
}
}
I took a few ideas from various answers here.我从这里的各种答案中获得了一些想法。
this implementation:这个实现:
JsonNullable.isJsonNull() == true
当JsonNullable.isJsonNull() == true
时发生JsonNullable.isJsonNull() == false
当JsonNullable.isJsonNull() == false
时发生Parent
containing JsonNullable
is null
包含JsonNullable
的Parent
中发生的字段为null
delegateAdapter
by using a TypeAdapterFactory
使用TypeAdapterFactory
将未处理的工作正确委托给delegateAdapter
objects that may need to be serialized to null implement this interface可能需要序列化为 null 的对象实现此接口
/**
* [JsonNullableTypeAdapterFactory] needs to be registered with the [com.google.gson.Gson]
* serializing implementations of [JsonNullable] for [JsonNullable] to work.
*
* [JsonNullable] allows objects to choose at runtime whether they should be serialized as "null"
* serialized normally, or be omitted from the JSON output from [com.google.gson.Gson].
*
* when [isJsonNull] returns true, the subclass will be serialized to a [com.google.gson.JsonNull].
*
* when [isJsonNull] returns false, the subclass will be serialized normally.
*/
interface JsonNullable {
/**
* return true to have the entire object serialized as `null` during JSON serialization.
* return false to have this object serialized normally.
*/
fun isJsonNull(): Boolean
}
type adapter factory that serializes values to null将值序列化为 null 的类型适配器工厂
class JsonNullableTypeAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
return object : TypeAdapter<T>() {
private val delegateAdapter = gson.getDelegateAdapter(this@JsonNullableTypeAdapterFactory, type)
override fun read(reader: JsonReader): T = delegateAdapter.read(reader)
override fun write(writer: JsonWriter, value: T?) {
if (value is JsonNullable && value.isJsonNull()) {
val originalSerializeNulls = writer.serializeNulls
writer.serializeNulls = true
writer.nullValue()
writer.serializeNulls = originalSerializeNulls
} else {
delegateAdapter.write(writer, value)
}
}
}
}
}
register thr type adapter factroy with GSON向 GSON 注册 thr 类型适配器工厂
new GsonBuilder()
// ....
.registerTypeAdapterFactory(new JsonNullableTypeAdapterFactory())
// ....
.create();
example object that gets serialized to JSON序列化为 JSON 的示例对象
data class Parent(
val hello: Child?,
val world: Child?
)
data class Child(
val name: String?
) : JsonNullable {
override fun isJsonNull(): Boolean = name == null
}
Adding to the answer given by @Arvoreniad添加到@Arvoreniad 给出的答案
The two additions are resetting the null serialization state in the JsonWriter after setting to true for the output and to use the field naming policy from Gson for getting the field name.这两个添加是在将输出设置为 true 后重置 JsonWriter 中的空序列化状态,并使用 Gson 的字段命名策略来获取字段名称。
public class SerializeNullTypeAdapterFactory implements TypeAdapterFactory {
/**
* {@inheritDoc}
*/
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Field[] declaredFields = type.getRawType().getDeclaredFields();
List<String> nullableFields = new ArrayList<>();
List<String> nonNullableFields = new ArrayList<>();
FieldNamingStrategy fieldNamingStrategy = gson.fieldNamingStrategy();
for (Field declaredField : declaredFields) {
// The Gson FieldNamingStrategy will handle the @SerializedName annotation + casing conversions
final String fieldName = fieldNamingStrategy.translateName(declaredField);
if (declaredField.isAnnotationPresent(JsonNullable.class)) {
nullableFields.add(fieldName);
} else {
nonNullableFields.add(fieldName);
}
}
if (nullableFields.isEmpty()) {
return null;
}
TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
nonNullableFields.forEach((var name) -> {
if (jsonObject.has(name) && (jsonObject.get(name) instanceof JsonNull)) {
jsonObject.remove(name);
}
});
boolean serializeNulls = out.getSerializeNulls();
out.setSerializeNulls(true);
elementAdapter.write(out, jsonObject);
// Reset default (in case JsonWriter is reused)
out.setSerializeNulls(serializeNulls);
}
@Override
public T read(JsonReader in) throws IOException {
return delegateAdapter.read(in);
}
};
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.