我有一个看起来像这样的 JSON 对象
{
"foo":{
"bar":"bar",
"echo":"echo"
}
}
Run Code Online (Sandbox Code Playgroud)
但是我的 Java 对象看起来像这样:
{
"foo":{
"bar":"bar",
"echo":"echo"
}
}
Run Code Online (Sandbox Code Playgroud)
我想echo直接序列化为foo. 这样的事情可能吗:
class Foo {
public String foo2;
}
Run Code Online (Sandbox Code Playgroud)
或者我如何使用自定义解串器来做到这一点?
作为替代方法,您还可以创建自己的类型适配器,以便将 JSON 表达式应用于不存在的字段。如果您可以自由地向您正在处理的项目添加新库,它可以基于JsonPath。
拥有这样一个非标准类型的适配器,您可以省略直接绑定到缺失字段的中间映射类:
final class Foo {
// or @JsonPathExpression("foo.echo")
@JsonPathExpression("$.foo.echo")
String foo2;
}
Run Code Online (Sandbox Code Playgroud)
@JsonPathExpression是自定义注释,可以自己处理(JsonPath可以是较短的名称,但它已被 JsonPath 库占用,以免造成混淆):
@Retention(RUNTIME)
@Target(FIELD)
@interface JsonPathExpression {
String value();
}
Run Code Online (Sandbox Code Playgroud)
类型适配器允许编写复杂的序列化/反序列化策略,它们的特点之一是可以将它们组合起来编写后处理器,例如,可以处理自定义注释。
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在必须配置 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);
}
});
}
Run Code Online (Sandbox Code Playgroud)
以及它是如何使用的:
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);
Run Code Online (Sandbox Code Playgroud)
输出:
回声
{“foo2”:“回声”}
请注意,这种方法有两个缺点:
| 归档时间: |
|
| 查看次数: |
2734 次 |
| 最近记录: |