作爲一種替代方法,您也可以以JSON表達式應用到不存在的領域創建自己類型的適配器。如果您可以自由地將新庫添加到您正在處理的項目中,它可以基於JsonPath。
具有這樣的非標準型適配器,則可以省略中間映射類直接綁定到丟失的字段:
final class Foo {
// or @JsonPathExpression("foo.echo")
@JsonPathExpression("$.foo.echo")
String foo2;
}
@JsonPathExpression
是自定義註釋,它可以自己處理(JsonPath
可能是一個較短的名字,但它已經被JsonPath庫佔領,這樣不會使混亂):
@Retention(RUNTIME)
@Target(FIELD)
@interface JsonPathExpression {
String value();
}
型適配器允許編寫複雜的序列化/反序列化策略,其特點之一可以將它們組合起來編寫後處理程序,因此,例如,可以處理自定義註釋。
final class JsonPathTypeAdapterFactory
implements TypeAdapterFactory {
// The type adapter factory is stateless so it can be instantiated once
private static final TypeAdapterFactory jsonPathTypeAdapterFactory = new JsonPathTypeAdapterFactory();
private JsonPathTypeAdapterFactory() {
}
static TypeAdapterFactory getJsonPathTypeAdapterFactory() {
return jsonPathTypeAdapterFactory;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Pick up the down stream type adapter to avoid infinite recursion
final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, typeToken);
// Collect @JsonPathExpression-annotated fields
final Collection<FieldInfo> fieldInfos = FieldInfo.of(typeToken.getRawType());
// If no such fields found, then just return the delegated type adapter
// Otherwise wrap the type adapter in order to make some annotation processing
return fieldInfos.isEmpty()
? delegateAdapter
: new JsonPathTypeAdapter<>(gson, delegateAdapter, gson.getAdapter(JsonElement.class), fieldInfos);
}
private static final class JsonPathTypeAdapter<T>
extends TypeAdapter<T> {
private final Gson gson;
private final TypeAdapter<T> delegateAdapter;
private final TypeAdapter<JsonElement> jsonElementTypeAdapter;
private final Collection<FieldInfo> fieldInfos;
private JsonPathTypeAdapter(final Gson gson, final TypeAdapter<T> delegateAdapter, final TypeAdapter<JsonElement> jsonElementTypeAdapter,
final Collection<FieldInfo> fieldInfos) {
this.gson = gson;
this.delegateAdapter = delegateAdapter;
this.jsonElementTypeAdapter = jsonElementTypeAdapter;
this.fieldInfos = fieldInfos;
}
@Override
public void write(final JsonWriter out, final T value)
throws IOException {
// JsonPath can only read by expression, but not write by expression, so we can only write it as it is...
delegateAdapter.write(out, value);
}
@Override
public T read(final JsonReader in)
throws IOException {
// Building the original JSON tree to keep *all* fields
final JsonElement outerJsonElement = jsonElementTypeAdapter.read(in).getAsJsonObject();
// Deserialize the value, not-existing fields will be omitted
final T value = delegateAdapter.fromJsonTree(outerJsonElement);
for (final FieldInfo fieldInfo : fieldInfos) {
try {
// Resolving JSON element by a JSON path expression
final JsonElement innerJsonElement = fieldInfo.jsonPath.read(outerJsonElement);
// And convert it to the field type
final Object innerValue = gson.fromJson(innerJsonElement, fieldInfo.field.getType());
// Since now it's what can be assigned to the object field...
fieldInfo.field.set(value, innerValue);
} catch (final PathNotFoundException ignored) {
// if no path given, then just ignore the assignment to the field
} catch (final IllegalAccessException ex) {
throw new IOException(ex);
}
}
return value;
}
}
private static final class FieldInfo {
private final Field field;
private final JsonPath jsonPath;
private FieldInfo(final Field field, final JsonPath jsonPath) {
this.field = field;
this.jsonPath = jsonPath;
}
// Scan the given class for the JsonPathExpressionAnnotation
private static Collection<FieldInfo> of(final Class<?> clazz) {
Collection<FieldInfo> collection = emptyList();
for (final Field field : clazz.getDeclaredFields()) {
final JsonPathExpression jsonPathExpression = field.getAnnotation(JsonPathExpression.class);
if (jsonPathExpression != null) {
if (collection.isEmpty()) {
collection = new ArrayList<>();
}
field.setAccessible(true);
collection.add(new FieldInfo(field, compile(jsonPathExpression.value())));
}
}
return collection;
}
}
}
現在GSON和JsonPath都必須配置(後者不使用默認GSON):
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getJsonPathTypeAdapterFactory())
.create();
static {
final JsonProvider jsonProvider = new GsonJsonProvider(gson);
final MappingProvider gsonMappingProvider = new GsonMappingProvider(gson);
Configuration.setDefaults(new Configuration.Defaults() {
@Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
@Override
public MappingProvider mappingProvider() {
return gsonMappingProvider;
}
@Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
}
以及它如何使用:
final Foo foo = gson.fromJson("{\"foo\":{\"bar\":\"bar\",\"echo\":\"echo\"}}", Foo.class);
System.out.println(foo.foo2);
final String json = gson.toJson(foo);
System.out.println(json);
輸出:
echo
{「foo2的」:「回聲」}
注意,這種方法有兩個缺點:
- 它不能被用來編寫原來的JSON回來,由於根本原因一樣破壞原有的信息和JsonPath只讀語義。
- 如果給定的JSON文檔包含已由同名對象字段映射的對象屬性,則下游解析器(由上述類型適配器使用)具有較高的優先級,因此會導致JSON解析錯誤。
您發佈的json無效,密鑰必須是字符串 – lelloman