如何使Gson.fromJson()返回空列表而不是空值字段的null?

bcs8qyzn  于 2023-05-28  发布在  其他
关注(0)|答案(5)|浏览(399)

我正在使用Gson反序列化一堆对象,其中一些更复杂,一些“轻量级”。
我在这里遇到了一个难题,因为我一直在期待GSON.fromJson()在阅读一个值为null的字段时会有另一个行为。更准确地说,看看这个例子

public class TestApplication{
    @Data
    @NoArgsConstructor
    public class User{

        private String userName;
        @SerializedName(value = "eMailAddress")
        private String email;
        private final List<Object> list = new ArrayList<>();

    }

    public static void main(String[] args) {
        String x = "{\n" + "\"userName\": \"test\""
            + ",\n" + "\"eMailAddress\": \"dan@gmail.com\""
            + ",\n" + "\"list\": null\n"
            + "}";

        User from = new Gson().fromJson(x, User.class);
        System.out.println(from);
    }

}

这将打印:

TestApplication.User(userName=test, email=dan@gmail.com, list=null)

我希望输出列表为空(list=[])而不是null。
我能做些什么来达到我需要的行为吗?我的应用程序使用对象列表,为每个对象注册一个typeAdapter是不可行的。
我该如何编写一个自定义的反序列化器?下面这个例子如果能工作的话就足够简单了:

class CollectionDeserializer implements JsonDeserializer<Collection<?>> {

        public Collection<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

            return ObjectUtils.isEmpty(json) ? new ArrayList<>() : new Gson().fromJson(json, Collection.class);
        }

    }

我所尝试的:

  • 实现客户反序列化器(如上所述),但没有成功。
  • @JsonSetter(nulls= Nulls.SKIP)注解,对Gson解析没有影响。
56lgkhnf

56lgkhnf1#

您的JSON字符串显式地表示此列表为null,因此我不确定您为什么首先期望它为空。对于空字符串,JSON字符串应为[]
然而,如果您仍然想在自定义反序列化器中将空列表转换为空列表,答案很简单。您的自定义反序列化器不工作,因为它正在测试json元素是否为null或空。两者都不是它是一个表示空元素的值。使用JsonElement.isJsonNull:

class CollectionDeserializer implements JsonDeserializer<Collection<?>> {
    public Collection<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        return json.isJsonNull() ? Collections.emptyList() : ...;
    }
}
sdnqo3pr

sdnqo3pr2#

它可以通过Jackson来完成,并且不显式地将该字段设置为null:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

public class TestApplication{
    @Data
    @NoArgsConstructor
    public static class User{

        private String userName;
        @JsonProperty("eMailAddress")
        private String email;
        private final List<Object> list = new ArrayList<>();

    }

    public static void main(String[] args) throws JsonProcessingException {
        String x = "{\n" + "\"userName\": \"test\""
                + ",\n" + "\"eMailAddress\": \"dan@gmail.com\""
                + "}";

        ObjectMapper mapper = new ObjectMapper();
        User from = mapper.readValue(x, User.class);

        System.out.println(from);
    }

}

这将打印:

TestApplication.User(userName=test, email=dan@gmail.com, list=[])
yzckvree

yzckvree3#

这可以通过自定义TypeAdapterFactory来解决:

class NullListToEmptyFactory implements TypeAdapterFactory {
    public static final NullListToEmptyFactory INSTANCE = new NullListToEmptyFactory();

    private NullListToEmptyFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<?> rawType = type.getRawType();

        // Only handle List and ArrayList; let other factories handle different types
        if (rawType != List.class && rawType != ArrayList.class) {
            return null;
        }

        // Delegate which handles deserialization of non-null values, and serialization
        TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();

                    // Safe due to check at beginning of `create` method
                    @SuppressWarnings("unchecked")
                    T t = (T) new ArrayList<>();
                    return t;
                } else {
                    return delegate.read(in);
                }
            }
        };
    }
}

然后,您可以在GsonBuilder上注册此工厂,如下所示:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(NullListToEmptyFactory.INSTANCE)
    .create();

使用TypeAdapterFactory具有以下优点:

  • 它使用TypeAdapterTypeAdapter的性能通常优于JsonDeserializer,因为它直接对JSON流进行操作,而不必先将其转换为JsonElement
  • 它允许委派到默认适配器:
  • 不需要创建第二个Gson示例;反序列化将使用现有的Gson示例及其所有配置
  • 它支持自定义元素类型,例如List<MyClass>,因为委托执行所有类型解析工作
    **重要提示:**您选择的任何解决方案都不会处理JSON数据中缺少字段的情况。Gson目前还不太支持这个功能,参见this feature request。当前必须使用非nullList显式初始化字段(并确保类具有无参数构造函数;更多信息),否则,如果JSON数据中缺少该字段,则该字段将为null
ovfsdjhp

ovfsdjhp4#

这可以通过Gson来完成,并显式地将字段设置为空列表:替换:

+ ",\n" + "\"list\": null\n"

有:

+ ",\n" + "\"list\": []\n"

这将打印:

TestApplication.User(userName=test, email=null, list=[])
ycggw6v2

ycggw6v25#

空列表与空列表不同。
如果希望所有null数组都成为空数组,则只能使用自定义适配器。工作量很大您需要以下内容:
ArrayTypeAdapterCollectionTypeAdapterFactory和其他设置类的新变体。
更改ArrayTypeAdapter的以下行

if (in.peek() == JsonToken.NULL) {
  in.nextNull();
  return null;
}

if (in.peek() == JsonToken.NULL) {
  in.nextNull();
  return Array.newInstance(componentType, 0);
}

我建议您不要这样做,而是添加一个输入验证器@NotNull,或者在反序列化之后,针对特定值以编程方式将null更改为[]

相关问题