Aza*_*tov 5 java generics json gson
我想从客户端转移到服务器WHERE子句作为JSON.我已经创建FilterInfo.class并Filter.class在服务器上:
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;
}
Run Code Online (Sandbox Code Playgroud)
我filterInfo作为JSON的示例:
{
"filters": [
{ "fieldName" : "Name",
"operand" : "=",
"value" : "John" },
{ "fieldName" : "Age",
"operand" : ">=",
"value" : "30" }
],
"orderBy": "Age"
}
Run Code Online (Sandbox Code Playgroud)
那么在服务器和构建查询上阅读这个JSON应该很棒.
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.setDateFormat(Constants.MY_DATE_FORMAT)
.create();
FilterInfo filterInfo = gson.fromJson(jsonString, FilterInfo.class);
Run Code Online (Sandbox Code Playgroud)
不幸的是,值Date和Integer反序列化为String和Double.
我见过TypeToken自定义序列化器/反序列化器的例子,但无法猜测如何将它们应用于我.
如果你弄清楚我的错误,我会很高兴,并提出好主意.谢谢!
不幸的是,日期和整数值反序列化为字符串和双精度。
当您定义像 -- Gson 这样的泛型类型字段时,Field<T>Gson 无法获得有关如何将某个值反序列化为某个类型的足够信息。这是一个根本性的限制:没有类型信息。因此 Gson 解析<T>就像它被参数化为 一样<Object>。当考虑某个目标“槽”(列表元素、对象字段等)时java.lang.Object,Gson 根据值文字的类型解析 JSON 值:如果它类似于"...",那么它可能是String; 如果它是类似的0,那么它肯定是 aNumber并且更准确:(Double双精度数是最大的标准数值 - Gson 只是节省数字类型检测和解析的时间 + 用户代码应该具有java.util.List<Number>并检测特定的列表元素instanceof- 它可能是一个整数、一个长整型或一个双精度值——使用起来不太方便,所以java.lang.Double是默认策略)。这样您就可以拥有字符串和双精度数,而不是日期和整数:Gson simple本身无法拥有您想要的类型信息。
为什么不能直接使用类型标记:类型标记用于为相同类型的元素指定类型参数,因此即使对于二元素列表,也不能使用多个类型标记来覆盖不同类型(列表类型标记为所有元素定义类型)列表元素)。
为了完成您的需要,您可以创建一个类型适配器和相应的类型适配器工厂来执行某种查找来解析具体类型。说,
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
好的,那可以怎么用呢?我已经使用以下映射对其进行了测试:
final class Filter<T> {
final String fieldName = null;
final String operand = null;
final T value = null;
}
Run Code Online (Sandbox Code Playgroud)
final class FilterInfo {
final List<Filter<?>> filters = null;
final String orderBy = null;
}
Run Code Online (Sandbox Code Playgroud)
如果您可以在 JSON 中提供类型名称来查找过滤器类型,则示例 JSON 可能如下所示:
{
"filters": [
{"_type": "date", "fieldName": "fooDate", "operand": "=", "value": "1997-12-20"},
{"_type": "int", "fieldName": "barInteger", "operand": ">=", "value": 10}
],
"orderBy": "fooDate"
}
Run Code Online (Sandbox Code Playgroud)
现在Gson可以像这样构建实例:
{
"filters": [
{"_type": "date", "fieldName": "fooDate", "operand": "=", "value": "1997-12-20"},
{"_type": "int", "fieldName": "barInteger", "operand": ">=", "value": 10}
],
"orderBy": "fooDate"
}
Run Code Online (Sandbox Code Playgroud)
如果您不想增强 JSON 文档(这很好),那么您可以只替换策略,但是由于多种原因,解析类型可能会更加复杂,因为它强烈依赖于给定的过滤器值名称(相同的名称)可能用于不同类型):
{
"filters": [
{"fieldName": "fooDate", "operand": "=", "value": "1997-12-20"},
{"fieldName": "barInteger", "operand": ">=", "value": 10}
],
"orderBy": "fooDate"
}
Run Code Online (Sandbox Code Playgroud)
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();
Run Code Online (Sandbox Code Playgroud)
请注意,TypeTokens 和Types 可以被视为不可变和常量,因此可以将它们放在单独的类中:
{
"filters": [
{"fieldName": "fooDate", "operand": "=", "value": "1997-12-20"},
{"fieldName": "barInteger", "operand": ">=", "value": 10}
],
"orderBy": "fooDate"
}
Run Code Online (Sandbox Code Playgroud)
现在,对于这两种策略,以下代码
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();
Run Code Online (Sandbox Code Playgroud)
将输出:
fooDate
fooDate=Sat Dec 20 00:00:00 EET 1997 类 java.util.Date
barInteger>=10 类 java.lang.Integer
| 归档时间: |
|
| 查看次数: |
613 次 |
| 最近记录: |