JSON查询过滤器传输

时间:2017-03-27 12:19:44

标签: java json generics gson

我想从客户端转移到服务器WHERE子句作为JSON。 我在服务器上创建了VVV bbb, ccc HASHVVV

FilterInfo.class

我的Filter.class作为JSON的示例:

   public class Filter<T> {
      private String fieldName;
      private String operand;
      private T value; 
   }

   public class FilterInfo {
     private List<Filter> filters = new ArrayList<Filter>();
     private String orderBy;
   }

然后在服务器和构建查询上阅读此JSON应该很棒。

filterInfo

不幸的是,{ "filters": [ { "fieldName" : "Name", "operand" : "=", "value" : "John" }, { "fieldName" : "Age", "operand" : ">=", "value" : "30" } ], "orderBy": "Age" } Gson gson = new GsonBuilder() .setPrettyPrinting() .setDateFormat(Constants.MY_DATE_FORMAT) .create(); FilterInfo filterInfo = gson.fromJson(jsonString, FilterInfo.class); 值反序列化为DateInteger

我见过String的示例,自定义序列化器/反序列化器,但无法猜测如何将它们应用于我。

如果你弄清楚我的错误,我会很高兴,并提出好主意。 谢谢!

1 个答案:

答案 0 :(得分:2)

  

不幸的是,Date和Integer值反序列化为String和Double。

当您定义类似Field<T>的泛型类型字段时 - Gson无法获得有关如何将某个值反序列化为某种类型的足够信息。这是一个基本的限制:没有类型信息。因此,Gson解析<T>,就好像它被参数化为<Object>一样。当某个目标&#34; slot&#34; (列表元素,对象字段等)被认为是java.lang.Object,Gson根据值文字的类型解析JSON值:如果它类似"...",那么它就是&#39;可能是String;如果它类似0,那么它肯定是Number并且更准确:Double(双倍是最大的标准数值 - Gson只是节省时间在数字类型检测和解析+用户代码应该有java.util.List<Number>并检测具有instanceof的特定列表元素 - 它可能是一个整数,一个长值或一个双重值 - 不是非常有用使用,因此java.lang.Double是默认策略)。所以你有字符串和双精度而不是日期和整数:Gson simple 本身不能拥有你想要的类型信息

为什么你不能直接使用类型标记:类型标记用于为相同类型的元素指定类型参数,因此即使对于双元素,也不能有多个类型标记来覆盖不同的类型list(列表类型标记为所有列表元素定义类型)。

要完成您的需要,您可以创建一个类型适配器和相应的类型适配器工厂来执行某种查找以解决具体类型。说,

final class FilterTypeAdapterFactory
        implements TypeAdapterFactory {

    // This is a strategy up to your needs: resolve a java.lang.reflect.Type by a filter object content 
    private final Function<? super JsonObject, ? extends Type> typeResolvingStrategy;

    private FilterTypeAdapterFactory(final Function<? super JsonObject, ? extends Type> typeResolvingStrategy) {
        this.typeResolvingStrategy = typeResolvingStrategy;
    }

    static TypeAdapterFactory getFilterTypeAdapterFactory(final Function<? super JsonObject, ? extends Type> typeResolvingStrategy) {
        return new FilterTypeAdapterFactory(typeResolvingStrategy);
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Is it the Filter class?
        if ( Filter.class.isAssignableFrom(typeToken.getRawType()) ) {
            // Get the JsonObject type adapter
            final TypeAdapter<JsonObject> jsonObjectTypeAdapter = gson.getAdapter(JsonObject.class);
            // This is a function to resolve a downstream type adapter by the given type
            // If a downstream parser is not used, then the lookup will end up with self-recursion...
            final Function<Type, TypeAdapter<T>> typeTypeAdapterFunction = type -> {
                // Create a type token dynamically
                @SuppressWarnings("unchecked")
                final TypeToken<T> delegateTypeToken = (TypeToken<T>) TypeToken.get(type);
                // And get the downstream type adapter
                return gson.getDelegateAdapter(this, delegateTypeToken);
            };
            return new FilterTypeAdapter<>(jsonObjectTypeAdapter, typeTypeAdapterFunction, typeResolvingStrategy);
        }
        // Not a thing we can handle? Return null, and Gson will try to perform lookup itself
        return null;
    }

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

        private final TypeAdapter<JsonObject> jsonObjectTypeAdapter;
        private final Function<? super Type, ? extends TypeAdapter<T>> typeAdapterResolver;
        private final Function<? super JsonObject, ? extends Type> typeResolvingStrategy;

        private FilterTypeAdapter(
                final TypeAdapter<JsonObject> jsonObjectTypeAdapter,
                final Function<? super Type, ? extends TypeAdapter<T>> typeAdapterResolver,
                final Function<? super JsonObject, ? extends Type> typeResolvingStrategy
        ) {
            this.jsonObjectTypeAdapter = jsonObjectTypeAdapter;
            this.typeAdapterResolver = typeAdapterResolver;
            this.typeResolvingStrategy = typeResolvingStrategy;
        }

        @Override
        public void write(final JsonWriter out, final T value) {
            // If you ever need it, then you have to implement it
            throw new UnsupportedOperationException();
        }

        @Override
        public T read(final JsonReader in)
                throws IOException {
            // Read the next {...} and convert it to JsonObject
            final JsonObject jsonObject = jsonObjectTypeAdapter.read(in);
            // Now resolve a real type by the given JsonObject instance
            // ... and resolve its type adapter
            final TypeAdapter<T> delegateTypeAdapter = typeAdapterResolver.apply(typeResolvingStrategy.apply(jsonObject));
            // Since the reader has the {...} value already consumed, we cannot read it at this moment
            // But we can convert the cached JsonObject to the target type object
            return delegateTypeAdapter.fromJsonTree(jsonObject);
        }

    }

}

好的,怎么用?我用以下映射测试了它:

final class Filter<T> {

    final String fieldName = null;
    final String operand = null;
    final T value = null;

}
final class FilterInfo {

    final List<Filter<?>> filters = null;
    final String orderBy = null;

}

In-JSON类型名称策略

如果您可以在JSON中提供类型名称以查找过滤器类型,那么示例JSON可能如下所示:

{
    "filters": [
        {"_type": "date", "fieldName": "fooDate", "operand": "=", "value": "1997-12-20"},
        {"_type": "int", "fieldName": "barInteger", "operand": ">=", "value": 10}
    ],
    "orderBy": "fooDate"
}

现在可以像这样构建Gson实例:

private static final Gson gson = new GsonBuilder()
        .setDateFormat("yyyy-MM-dd")
        .registerTypeAdapterFactory(getFilterTypeAdapterFactory(jsonObject -> {
            if ( !jsonObject.has("_type") ) {
                return defaultFilterType;
            }
            switch ( jsonObject.get("_type").getAsString() ) {
            case "int":
                return integerFilterType;
            case "date":
                return dateFilterType;
            default:
                return defaultFilterType;
            }
        }))
        .create();

替代策略

如果你不想增强你的JSON文档(那很好),那么你可以直接替换策略,但由于几个原因解决类型可能会更复杂,因为它强烈依赖于给定的过滤器值名称(相同的名称可能用于不同的类型):

{
    "filters": [
        {"fieldName": "fooDate", "operand": "=", "value": "1997-12-20"},
        {"fieldName": "barInteger", "operand": ">=", "value": 10}
    ],
    "orderBy": "fooDate"
}
private static final Gson gson = new GsonBuilder()
        .setDateFormat("yyyy-MM-dd")
        .registerTypeAdapterFactory(getFilterTypeAdapterFactory(jsonObject -> {
            if ( !jsonObject.has("fieldName") ) {
                return defaultFilterType;
            }
            switch ( jsonObject.get("fieldName").getAsString() ) {
            case "barInteger":
                return integerFilterType;
            case "fooDate":
                return dateFilterType;
            default:
                return defaultFilterType;
            }
        }))
        .create();

请注意,TypeTokenType可以被视为不可变且不变,因此可以将它们放在一个单独的类中:

final class Types {

    private Types() {
    }

    static final Type defaultFilterType = new TypeToken<Filter<Object>>() {
    }.getType();

    static final Type integerFilterType = new TypeToken<Filter<Integer>>() {
    }.getType();

    static final Type dateFilterType = new TypeToken<Filter<Date>>() {
    }.getType();

}

现在,对于两个stragies,以下代码

final FilterInfo filterInfo = gson.fromJson(JSON, FilterInfo.class);
System.out.println(filterInfo.orderBy);
for ( final Filter filter : filterInfo.filters ) {
    System.out.println(filter.fieldName + filter.operand + filter.value + " of " + filter.value.getClass());
}

将输出:

  

fooDate
  fooDate =星期六十二月20 00:00:00 EET 1997类java.util.Date
  barInteger&gt; = java.lang.Integer类中的10个