gpt4 book ai didi

java - 反序列化JSON时如何区分not-provided和null

转载 作者:行者123 更新时间:2023-11-30 10:29:06 31 4
gpt4 key购买 nike

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

  • 有时他想传递一个新的值,后端简单地写入数据库
  • 有时他想传递no值,告诉后端不要对这个值做任何事情
  • 有时他想传递一个null,告诉后端重置到某个“默认值”(后端已知,但前端不知道)关心它)
  • 它也应该适用于字符串、数字、 boolean 值......

我们想出了这个主意:

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

请注意:这个想法当然是在一个 bean 中有多个该类型 ResetableValue 的不同字段。然后一个字段可能关心一个值,一个被省略,另一个被设置为空。

问题:

  • 上面的示例“有效”——但我真的不喜欢这样一个事实,即我必须在我的 bean 的 getProperty() 方法中处理“重置”情况。这意味着:拥有自定义反序列化器是不够的,我还需要将该特殊检查放入任何 getter 方法中。那么:对此有更优雅的解决方案吗?有没有办法让 Gson 区分“属性未显示”与“属性设置为空”之间的区别?
  • 上面的例子声称是通用的;但显然反序列化程序代码仅适用于字符串属性。有没有办法让它“真正通用”?

我想表达我的问题的另一种方式是:在使用 Gson 将 JSON 反序列化为 bean 时是否支持“Optionals”之类的东西?

最佳答案

The above example "works" - but I really dislike the fact that I have to handle the "reset" case within the getProperty() method of my bean. As that means: it is not enough to have a custom deserializer, I also need to put that special check into any getter method. So: are there more elegant solutions to this? Is there a way to have Gson distinguish between "a property is not showing up" vs. "a property is set to null"?

有点。你的 getProperty 似乎有一个多余的检查:它不应该检查 null 并且在任何情况下只返回 property 字段假设 Gson 设法实例化它。

The above example claims to be generic; but obviously the deserializer code only works for string properties. Is there a way to make this "really generic"?

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

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

final class Value<T> {

private static final Value<?> noValue = new Value<>(State.NO_VALUE, null);
private static final Value<?> undefined = new Value<>(State.UNDEFINED, null);

private enum State {

VALUE,
NO_VALUE,
UNDEFINED

}

private final State state;
private final T value;

private Value(final State state, final T value) {
this.value = value;
this.state = state;
}

static <T> Value<T> value(final T value) {
if ( value == null ) {
return noValue();
}
return new Value<>(State.VALUE, value);
}

static <T> Value<T> noValue() {
@SuppressWarnings("unchecked")
final Value<T> value = (Value<T>) noValue;
return value;
}

static <T> Value<T> undefined() {
@SuppressWarnings("unchecked")
final Value<T> value = (Value<T>) undefined;
return value;
}

T getValue()
throws IllegalStateException {
if ( state != State.VALUE ) {
throw new IllegalStateException("Can't provide value for " + state);
}
return value;
}

boolean isValue() {
return state == State.VALUE;
}

boolean isNoValue() {
return state == State.NO_VALUE;
}

boolean isUndefined() {
return state == State.UNDEFINED;
}

@Override
public String toString() {
if ( state != State.VALUE ) {
return state.toString();
}
return Objects.toString(value);
}

}

接下来,定义一个简单的数据包来保存这些值。请注意,您必须将它们声明为 undefined() 以保留空对象语义,或者将其分配给 null 以便 Gson 在下面处理它(您选择)。

final class DataBag {

final Value<Integer> integer = null;/* = undefined()*/
final Value<String> string = null;/* = undefined()*/

private DataBag() {
}

}

一些反射实用程序代码在这里分析类型参数化和构建子类到父类(super class)层次结构迭代器(我还不知道如何从头开始创建 Java 8 流):

final class Reflection {

private Reflection() {
}

static Type getTypeParameter0(final Type type) {
if ( !(type instanceof ParameterizedType) ) {
return Object.class;
}
final ParameterizedType parameterizedType = (ParameterizedType) type;
return parameterizedType.getActualTypeArguments()[0];
}

static Iterable<Class<?>> subToSuperClass(final Class<?> subClass) {
return subToSuperClass(Object.class, subClass);
}

static <SUP, SUB extends SUP> Iterable<Class<?>> subToSuperClass(final Class<SUP> superClass, final Class<SUB> subClass) {
if ( !superClass.isAssignableFrom(subClass) ) {
throw new IllegalArgumentException(superClass + " is not assignable from " + subClass);
}
return () -> new Iterator<Class<?>>() {
private Class<?> current = subClass;

@Override
public boolean hasNext() {
return current != null;
}

@Override
public Class<?> next() {
if ( current == null ) {
throw new NoSuchElementException();
}
final Class<?> result = current;
current = result != superClass ? current.getSuperclass() : null;
return result;
}
};
}


}

值类型适配器工厂

ValueTypeAdapterFactory 通过将(反)序列化过程委托(delegate)给下游类型适配器来负责任何通用值。

final class ValueTypeAdapterFactory
implements TypeAdapterFactory {

private static final TypeAdapterFactory valueTypeAdapterFactory = new ValueTypeAdapterFactory();

private ValueTypeAdapterFactory() {
}

static TypeAdapterFactory getValueTypeAdapterFactory() {
return valueTypeAdapterFactory;
}

@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !Value.class.isAssignableFrom(typeToken.getRawType()) ) {
return null;
}
final Type valueTypeParameter = getTypeParameter0(typeToken.getType());
// Some boring Java unchecked stuff here...
@SuppressWarnings("unchecked")
final TypeAdapter<Object> innerTypeAdapter = (TypeAdapter<Object>) gson.getDelegateAdapter(this, TypeToken.get(valueTypeParameter));
final TypeAdapter<Value<Object>> outerTypeAdapter = new ValueTypeAdapter<>(innerTypeAdapter);
@SuppressWarnings("unchecked")
final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) outerTypeAdapter;
return typeAdapter;
}

private static final class ValueTypeAdapter<T>
extends TypeAdapter<Value<T>> {

private final TypeAdapter<T> innerTypeAdapter;

private ValueTypeAdapter(final TypeAdapter<T> innerTypeAdapter) {
this.innerTypeAdapter = innerTypeAdapter;
}

@Override
public void write(final JsonWriter out, final Value<T> value)
throws IOException {
if ( value.isValue() ) {
final T innerValue = value.getValue();
innerTypeAdapter.write(out, innerValue);
return;
}
// Considering no-value is undefined in order not to produce illegal JSON documents (dangling property names, etc)
if ( value.isNoValue() || value.isUndefined() ) {
innerTypeAdapter.write(out, null);
return;
}
throw new AssertionError();
}

@Override
public Value<T> read(final JsonReader in)
throws IOException {
final JsonToken token = in.peek();
if ( token == NULL ) {
in.nextNull();
return noValue();
}
return value(innerTypeAdapter.read(in));
}

}

}

PostValueTypeAdapterFactory

PostValueTypeAdapterFactory 负责使用反射“调整”具有 null 初始化的 Value 字段的 POJO。通过不注册这个工厂,所有 Value 字段都必须用 undefined() 手动初始化。为简单起见,此处未实现任何顺序数据结构,如 iterables/collections/lists|sets、maps 或 arrays。

final class PostValueTypeAdapterFactory
implements TypeAdapterFactory {

private static final TypeAdapterFactory postValueTypeAdapterFactory = new PostValueTypeAdapterFactory();

private PostValueTypeAdapterFactory() {
}

static TypeAdapterFactory getPostValueTypeAdapterFactory() {
return postValueTypeAdapterFactory;
}

@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final List<Field> valueFields = collectValueFields(typeToken.getRawType());
if ( valueFields.isEmpty() ) {
return null;
}
final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
return new PostValueTypeAdapter<>(delegateTypeAdapter, valueFields);
}

// Just scan class the whole type hierarchy (except java.lang.Object) to find any occurrences of Value<T> fields
private static List<Field> collectValueFields(final Class<?> type) {
return StreamSupport.stream(subToSuperClass(type).spliterator(), false)
.filter(clazz -> clazz != Object.class)
.flatMap(clazz -> Stream.of(clazz.getDeclaredFields()))
.filter(field -> field.getType() == Value.class)
.peek(field -> field.setAccessible(true))
.collect(toImmutableList());
}

private static final class PostValueTypeAdapter<T>
extends TypeAdapter<T> {

private final TypeAdapter<T> delegateTypeAdapter;
private final List<Field> valueFields;

private PostValueTypeAdapter(final TypeAdapter<T> delegateTypeAdapter, final List<Field> valueFields) {
this.delegateTypeAdapter = delegateTypeAdapter;
this.valueFields = valueFields;
}

@Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegateTypeAdapter.write(out, value);
}

@Override
public T read(final JsonReader in)
throws IOException {
try {
final T value = delegateTypeAdapter.read(in);
for ( final Field valueField : valueFields ) {
// A Value<T> field is null? Make it undefined
if ( valueField.get(value) == null ) {
valueField.set(value, undefined());
}
}
return value;
} catch ( final IllegalAccessException ex ) {
throw new IOException(ex);
}
}

}

}

JUnit 测试:

public final class GsonStuffTest {

private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getValueTypeAdapterFactory())
.registerTypeAdapterFactory(getPostValueTypeAdapterFactory())
.create();

@Test
public void testIsValue() {
final DataBag dataBag = parseDataBag("{\"integer\":100,\"string\":\"foo\"}");
assertThat(dataBag.integer.isValue(), is(true));
assertThat(dataBag.integer.getValue(), is(100));
assertThat(dataBag.string.isValue(), is(true));
assertThat(dataBag.string.getValue(), is("foo"));
}

@Test
public void testIsNoValue() {
final DataBag dataBag = parseDataBag("{\"integer\":null,\"string\":null}");
assertThat(dataBag.integer.isNoValue(), is(true));
assertThat(dataBag.string.isNoValue(), is(true));
}

@Test
public void testIsUndefined() {
final DataBag dataBag = parseDataBag("{}");
assertThat(dataBag.integer.isUndefined(), is(true));
assertThat(dataBag.string.isUndefined(), is(true));
}

private static DataBag parseDataBag(final String json) {
return gson.fromJson(json, DataBag.class);
}

}

关于java - 反序列化JSON时如何区分not-provided和null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44249136/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com