我有一个看起来像这样的JSON对象
{
"foo":{
"bar":"bar",
"echo":"echo"
}
}
但是我的Java对象看起来像这样:
class Foo {
public String foo2;
}
我想将echo
直接序列化为foo
。这样的事情是可能的:
class Foo {
@SerializedName("foo/echo")
public String foo2;
}
或者如何使用自定义反序列化程序执行此操作?
答案 0 :(得分:2)
作为替代方法,您还可以创建自己的类型适配器,以便将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);
输出:
回波
{“foo2”:“echo”}
请注意,这种方法有两个缺点:
答案 1 :(得分:1)
我假设你使用GSON。为JSONObject创建不同的类。
public class FooModel {
@SerializedName("foo")
public Foo foo;
public class Foo {
@SerializedName("bar")
public String Bar;
@SerializedName("echo")
public String Echo;
}
}
答案 2 :(得分:1)
不幸的是,您无法使用@SerializedName
来执行此操作,因为它在流式解析中使用,因此Gson无法进行任何前瞻以解析路径表达式。然而,这个想法会很好,但它至少需要一个子树存储在内存中,在某些情况下可能太耗费内存。由于JsonSerializer
和JsonDeserializer
仅适用于JSON树,因此您可以轻松编写自己的JSON反序列化器,它可以省略不必要的JSON对象(在语义上等同于您希望在@SerializedName
中使用的表达式) 。所以,
// To unwrap the top-most JSON object
final class Wrapper {
Foo foo;
}
final class Foo {
String foo2;
}
反序列化器可以像这样实现(但是你应该记住,JsonSerializer
和JsonDeserializer
不能使用实际可以处理{1}的Gson内置ReflectiveTypeAdapterFactory
}}):
@SerializedName
使用示例:
final class FooJsonDeserializer
implements JsonDeserializer<Foo> {
private static final JsonDeserializer<Foo> fooJsonDeserializer = new FooJsonDeserializer();
private FooJsonDeserializer() {
}
static JsonDeserializer<Foo> getFooJsonDeserializer() {
return fooJsonDeserializer;
}
@Override
public Foo deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) {
final JsonObject jsonObject = jsonElement.getAsJsonObject();
final Foo foo = new Foo();
foo.foo2 = jsonObject.get("echo").getAsString();
return foo;
}
}
输出:
回波
答案 3 :(得分:0)
您必须编写包含所有变量的模型类,然后才能使用
Gson gson=new Gson();
ClassName objectName=gson.fromJson(yourJsonObject,ClassName.class);
此处objectName
包含您的json
答案 4 :(得分:0)
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Example {
@SerializedName("foo")
@Expose
private Foo foo;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
}
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Foo {
@SerializedName("bar")
@Expose
private String bar;
@SerializedName("echo")
@Expose
private String echo;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String getEcho() {
return echo;
}
public void setEcho(String echo) {
this.echo = echo;
}
}
您可以找到更多详情here
答案 5 :(得分:0)
是的,你可以这样做
添加此导入
import com.google.gson.annotations.SerializedName;
并声明这个变量
@SerializedName("echo")
private String myCustomeName;