Java 反序列化JSON时如何区分not PROFERED和null

Java 反序列化JSON时如何区分not PROFERED和null,java,json,generics,serialization,gson,Java,Json,Generics,Serialization,Gson,我们从前端发送JSON字符串作为java代码的输入。Java端使用Gson将其转换为bean。现在,我的前端人员向我提出以下要求: 有时他希望传递一个新值,后端只需将其写入数据库 有时他希望传递no值,这告诉后端不要对该值做任何事情 有时,他希望传递一个null,它告诉后端重置为某个“默认值”(后端已知,但前端不关心) 它还应适用于字符串、数字、布尔值等 我们想出了这个主意: import static org.hamcrest.Matchers.is; import static org.

我们从前端发送JSON字符串作为java代码的输入。Java端使用Gson将其转换为bean。现在,我的前端人员向我提出以下要求:

  • 有时他希望传递一个新值,后端只需将其写入数据库
  • 有时他希望传递no值,这告诉后端不要对该值做任何事情
  • 有时,他希望传递一个null,它告诉后端重置为某个“默认值”(后端已知,但前端不关心)
  • 它还应适用于字符串、数字、布尔值等
我们想出了这个主意:

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.lang.reflect.Type;
import java.util.Objects;
import org.junit.Test;
import com.google.gson.*;

class ResetableValue<T> {
    private static enum Content {
        VALUE, RESET, NOT_PROVIDED
    };
    private final T value;
    private final Content content;

    public ResetableValue(T value) {
        this(value, Content.VALUE);
    }
    private ResetableValue(T value, Content content) {
        this.value = value;
        this.content = content;
    }
    static <T> ResetableValue<T> asReset() {
        return new ResetableValue<>(null, Content.RESET);
    }
    static <T> ResetableValue<T> asNotProvided() {
        return new ResetableValue<>(null, Content.NOT_PROVIDED);
    }
    T getValue() {
        if (content != Content.VALUE) {
            throw new IllegalStateException("can't provide value for " + content);
        }
        return value;
    }
    boolean isReset() {
        return content == Content.RESET;
    }
    boolean isNotProvided() {
        return content == Content.NOT_PROVIDED;
    }
    @Override
    public String toString() {
        if (content == Content.VALUE) {
            return Objects.toString(value);
        }
        return content.toString();
    }
}

class ResetableValueDeserializer implements JsonDeserializer<ResetableValue<String>> {
    public ResetableValue<String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        return new ResetableValue<String>(json.getAsJsonPrimitive().getAsString());
    }
}
class ExampleBean {
    private ResetableValue<String> property = ResetableValue.asNotProvided();
    public ResetableValue<String> getProperty() {
        if (property == null) {
            return ResetableValue.asReset();
        }
        return property;
    }
    @Override
    public String toString() {
        return "property: " + Objects.toString(property);
    }
}

public class GsonStuffTest {
    Gson gson = new GsonBuilder().registerTypeAdapter(ResetableValue.class, new ResetableValueDeserializer()).create();

    @Test
    public void testValue() {
        String serializedContent = "{\"property\":\"foo\"}";
        ExampleBean bean = gson.fromJson(serializedContent, ExampleBean.class);
        assertThat(bean.getProperty().getValue(), is("foo"));
    }
    @Test
    public void testIsNotProvided() {
        String serializedContent = "{}";
        ExampleBean bean = gson.fromJson(serializedContent, ExampleBean.class);
        assertThat(bean.getProperty().isNotProvided(), is(true));
    }
    @Test
    public void testIsReset() {
        String serializedContent = "{\"property\":null}";
        ExampleBean bean = gson.fromJson(serializedContent, ExampleBean.class);
        assertThat(bean.getProperty().isReset(), is(true));
    }
}
import static org.hamcrest.Matchers.is;
导入静态org.junit.Assert.assertThat;
导入java.lang.reflect.Type;
导入java.util.Objects;
导入org.junit.Test;
导入com.google.gson.*;
类可重置值{
私有静态枚举内容{
值,重置,未提供
};
私人最终T值;
私人最终内容;
公共可重置值(T值){
这(价值、内容、价值);
}
私有可重置值(T值、内容){
这个值=值;
this.content=内容;
}
静态可重置值asReset(){
返回新的可重置值(null,Content.RESET);
}
静态可重置值AsNotProvidered(){
返回新的可重置值(空,内容。未提供);
}
T getValue(){
if(content!=content.VALUE){
抛出新的IllegalStateException(“无法为“+内容”提供值);
}
返回值;
}
布尔isReset(){
返回内容==content.RESET;
}
布尔值未提供(){
返回内容==content.NOT_提供;
}
@凌驾
公共字符串toString(){
if(content==content.VALUE){
返回Objects.toString(值);
}
返回content.toString();
}
}
类ResetableValueDeserializer实现JsonDeserializer{
公共可重置值反序列化(JsonElement json,类型typeOfT,JsonDeserializationContext)
抛出JsonParseException{
返回新的可重置值(json.getAsJsonPrimitive().getAsString());
}
}
类ExampleBean{
private ResetableValue属性=ResetableValue.asNotProvided();
公共可重置值getProperty(){
if(属性==null){
返回ResetableValue.asReset();
}
归还财产;
}
@凌驾
公共字符串toString(){
返回“property:+Objects.toString(property);
}
}
公共类gsonstuff测试{
Gson Gson=new GsonBuilder().registerTypeAdapter(ResetableValue.class,new ResetableValueDeserializer()).create();
@试验
公共void testValue(){
字符串serializedContent=“{\”属性\:\“foo\”}”;
ExampleBean=gson.fromJson(serializedContent,ExampleBean.class);
资产(bean.getProperty().getValue(),是(“foo”);
}
@试验
提供的公共无效测试(){
字符串serializedContent=“{}”;
ExampleBean=gson.fromJson(serializedContent,ExampleBean.class);
断言(bean.getProperty().isNotProvidered(),is(true));
}
@试验
公共无效测试集(){
String serializedContent=“{\'属性\”:null}”;
ExampleBean=gson.fromJson(serializedContent,ExampleBean.class);
断言(bean.getProperty().isReset()为(true));
}
}
请注意:这个想法当然是在一个bean中有多个不同类型的字段
ResetableValue
。然后一个字段可能关心一个值,一个被省略,另一个被设置为null

问题:
  • 上面的例子“有效”——但我真的不喜欢这样一个事实:我必须在bean的
    getProperty()
    方法中处理“reset”情况。这意味着:仅拥有自定义反序列化器是不够的,我还需要将该特殊检查放入任何getter方法中。那么:有没有更优雅的解决方案?是否有办法让Gson区分“属性未显示”和“属性设置为空”
  • 上述示例声称是通用的;但显然,反序列化程序代码只适用于字符串属性。有没有办法让这个“真正通用”呢
我想表达我的问题的另一种方式是:当使用Gson将JSON反序列化为bean时,是否有类似“Optionals”的支持

上面的例子“有效”——但我真的不喜欢这样一个事实,即我必须在bean的getProperty()方法中处理“reset”情况。这意味着:仅拥有自定义反序列化器是不够的,我还需要将该特殊检查放入任何getter方法中。那么:有没有更优雅的解决方案?是否有办法让Gson区分“属性未显示”和“属性设置为空”

有点。您的
getProperty
似乎有一个冗余检查:它不应该检查
null
,只要在任何情况下返回
property
字段,前提是Gson设法实例化它

上述示例声称是通用的;但显然,反序列化程序代码只适用于字符串属性。有没有办法让这个“真正通用”呢

是的,通过类型适配器工厂和类型适配器(关于后者:
JsonSerializer
JsonDeserializer
类使用JSON树消耗更多内存,但类型适配器是流式的,消耗更少)

让我们假设你有一个通用的三态值持有者,如下所示。 我还将隐藏构造函数,使其更流畅,并封装其实例化(或未实例化)的方式

最终类值{
私有静态最终值noValue=新值(State.NO_值,null);
私有的