在反序列化JSON

时间:2017-05-29 19:12:11

标签: java json generics serialization gson

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

  • 有时他想传递 new 值,后端只是写入数据库
  • 有时候他想传递没有值,这会告诉后端不对此值做任何事情
  • 有时他想传递一个 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));
    }
}

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

问题(S):

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

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

1 个答案:

答案 0 :(得分:1)

  

上面的例子“有效” - 但我真的不喜欢我必须在我的bean的getProperty()方法中处理“重置”的情况。这意味着:拥有自定义反序列化器是不够的,我还需要将特殊检查放入任何getter方法。那么:有更优雅的解决方案吗?有没有办法让Gson区分“财产没有出现”与“财产设置为空”?

排序。您的getProperty似乎有一个冗余检查:它应该永远不会检查null并且在任何情况下只返回property字段,假设Gson设法实例化它。

  

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

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

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

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() {
    }

}

这里有一些反射实用程序代码来分析类型参数化并构建子到超类层次结构迭代器(我不知道如何从头开始创建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

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

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

}