如何将JSON对象子序列化为字段?

时间:2017-02-28 08:44:32

标签: android gson

我有一个看起来像这样的JSON对象

{
  "foo":{
      "bar":"bar",
      "echo":"echo"
  }
}

但是我的Java对象看起来像这样:

class Foo {
    public String foo2;
}

我想将echo直接序列化为foo。这样的事情是可能的:

class Foo {
    @SerializedName("foo/echo")
    public String foo2;
}

或者如何使用自定义反序列化程序执行此操作?

6 个答案:

答案 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”}

请注意,这种方法有两个缺点:

  • 由于破坏原始信息和JsonPath只读语义等基本原因,它无法用于编写原始JSON。
  • 如果给定的JSON文档包含已由同名对象字段映射的对象属性,则下游解析器(由上面的类型适配器使用)具有更高的优先级,因此导致JSON解析错误。

答案 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无法进行任何前瞻以解析路径表达式。然而,这个想法会很好,但它至少需要一个子树存储在内存中,在某些情况下可能太耗费内存。由于JsonSerializerJsonDeserializer仅适用于JSON树,因此您可以轻松编写自己的JSON反序列化器,它可以省略不必要的JSON对象(在语义上等同于您希望在@SerializedName中使用的表达式) 。所以,

// To unwrap the top-most JSON object
final class Wrapper {   
    Foo foo;
}
final class Foo {   
    String foo2;    
}

反序列化器可以像这样实现(但是你应该记住,JsonSerializerJsonDeserializer不能使用实际可以处理{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;