当前位置: 首页 > 知识库问答 >
问题:

Gson为特定类或字段序列化null

空成天
2023-03-14

我想序列化特定字段或类的空值。

在GSON中,serializeNulls()选项适用于整个JSON。

例子:

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;    

使用GSON创建JSON:

GsonBuilder builder = new GsonBuilder().serializeNulls();
Gson gson = builder.create();
System.out.println(gson.toJson(mainClass));

电流输出:

{
    "id": "101",
    "name": null,
    "test": {
        "name": "testName",
        "value": null
    }
}

期望输出:

{
    "id": "101",
    "test": {
        "name": "testName",
        "value": null
    }
}

如何达到预期的输出?

首选解决方案应具有以下特性:

  • 默认情况下不要序列化空值
  • 为具有特定注释的字段序列化空值。

共有3个答案

阴宏爽
2023-03-14

对于那些寻找@Joris优秀答案的Java版本的人来说,下面的代码应该可以做到这一点。它基本上只是Kotlin的一个翻译,对获取属性的序列化名称的方式有一个小小的改进,以确保在序列化名称不同于属性名称时,它始终有效(请参阅原始答案上的注释)。

这是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);
                    }
                }

                out.setSerializeNulls(true);
                elementAdapter.write(out, jsonObject);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                return delegateAdapter.read(in);
            }

        };
    }
}

这是标记目标属性的@JsonNullable注释:

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

我将其实现为对象类上的@JsonAdapter(NullableAdapterFactory.class)注释,而不是将其注册为GsonBuilder实例上的TypeAdapterFactory,因此我的对象类看起来有点像这样:

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

  @JsonNullable
  public String someNullableInfo;
}

然而,如果愿意的话,另一种方法也应该适用于此代码。

辛龙野
2023-03-14

我有接口来检查对象何时应序列化为空:

public interface JsonNullable {
  boolean isJsonNull();
}

以及相应的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;
  }
}

使用方法如下:

public class Foo implements JsonNullable {
  @Override
  public boolean isJsonNull() {
    // You decide
  }
}

在类中,Foo值应序列化为null。请注意,foo值本身不能为null,否则将忽略自定义适配器注释。

public class Bar {
  @JsonAdapter(JsonNullableAdapter.class)
  public Foo foo = new Foo();
}
壤驷棋
2023-03-14

我有一个类似于Aleksey的解决方案,但可以应用于任何类中的一个或多个字段(例如Kotlin):

为应序列化为null的字段创建新注释:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class SerializeNull

创建一个TypeAdapterFactory,检查一个类是否有用此注释注释的字段,并在编写对象时从JsonTree中删除没有用注释注释的null字段:

kotlin prettyprint-override">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)
            }
        }
    }
}

向Gson实例注册适配器:

val builder = GsonBuilder().registerTypeAdapterFactory(SerializableAsNullConverter())

并注释您希望为空的字段:

class MyClass(val id: String?, @SerializeNull val name: String?)

序列化结果:

val myClass = MyClass(null, null)
val gson = builder.create()
val json = gson.toJson(myClass)

json:

{
    "name": null
}
 类似资料:
  • 问题内容: 我想基于字段值将json对象反序列化为特定类型的对象(使用Gson库),例如: 因此,字段将具有不同(但已知)的值。基于该值,我需要将该json对象反序列化为适当的模型对象,例如:Type1Model,Type2Model等。我知道我可以轻松地在反序列化之前通过将其转换为,迭代并确定应该反序列化为哪种类型来完成此操作。 。但是我认为这是丑陋的方法,我正在寻找更好的方法。有什么建议? 问

  • 首先,我想说这是一个大学项目。 我有三节课<代码>订单抽象类和从订单继承的和类。 我使用Gson序列化/反序列化子类,但我遇到了一点问题。订单类有一个字段,gson使用该字段来确定订单类型,DineIn或Delivery。 序列化工作得很好。问题是,每当我尝试反序列化时,类型字段值都不会读取,并且总是设置为null,即使它存在于JSON文件中。当有很多字段时,就会发生这种情况,因为当我尝试在较小的

  • 问题内容: 我有以下JSON字符串: 我需要将值存储在单独的字符串中。 这是我到目前为止所拥有的,但这只是为我提供了整个JSON字符串,而不是特定的imgurl字段。 是JSON字符串。这是我的数据类: 问题答案: 解析这种简单的结构时,不需要专用的类。 解决方案1: 要使用gson从字符串中获取imgurURL,可以执行以下操作: 这使用原始解析到JsonObject中。 解决方案2: 另外,您

  • 这是我目前所拥有的,但这只给了我整个JSON字符串,而不是特定的imgurl字段。 是JSON字符串。下面是我的数据类:

  • 是否有方法序列化类的瞬态字段?文档中提到默认情况下不支持它,但是否有办法打开它? 非常感谢

  • 我试图使用Gson自定义序列化将以下类序列化为Json。