使用自定义typeadapter,json中的jsonwriter添加对象

时间:2013-06-09 01:54:52

标签: java json gson

gson是一个很棒的图书馆 - 它运作良好。有时我有自定义要求,可以制作和注册TypeAdapters和TypeAdaptorFactories - 这也很有效。

然而令我困惑的是,如何委托回json序列化...大多数时候我需要这个用于集合,但为了说明这一点 - 假设我有一个对类,gson显然会愉快地序列化,但是出于某种原因,我需要自己的自定义序列化器。好吧......如果我的配对是

public class Pair
{
    public final Object First;
    public final Object Second; 
    public Pair( Object first, Object second) {this.first = first; this.second = second};
}

如果我为此编写了一个类型适配器 - 你将想要写函数看起来像:

public void write( JsonWriter out, Pair pair )
{
    out.beginObject();
    out.name( "first");
    out.value( pair.first );         // can't do this
    out.name( "second");
    out.value( pair.second);         // or this
    out.endObject();
}

所以你可以看到问题 - 我不知道第一个和第二个的类型,以及它们是如何序列化的。我可以使用gson.toJson来序列化第一个和第二个 - 但是如果我将它们作为字符串添加到编写器中,它们将被转义。有一个gson.tojson函数,它接受一个值和一个编写器 - 但它也需要一个typetoken - 我没有。我得到的印象是,我打算从某个地方安装另一个类型的适配器 - 但是当我只有一个对象列表时......我从哪里得到它?我只是获得对象的适配器吗?

我有点困惑吗?当然这是最常见的用例?大多数自定义序列化程序将用于T或T树之类的奇怪列表,并且你真的不知道列表中的内容,除了它继承自T ...所以你需要能够委托回来以某种方式序列化?

无论如何 - 如果有人能告诉我如何编写上述功能,我真的很感激!

2 个答案:

答案 0 :(得分:25)

在这种情况下,最好使用JsonSerializer而不是TypeAdapter,原因很简单,因为序列化程序可以访问其序列化上下文:

public class PairSerializer implements JsonSerializer<Pair> {

    public PairSerializer() {
        super();
    }

    @Override
    public JsonElement serialize(final Pair value, final Type type,
            final JsonSerializationContext context) {
        final JsonObject jsonObj = new JsonObject();
        jsonObj.add("first", context.serialize(value.getFirst()));
        jsonObj.add("second", context.serialize(value.getSecond()));

        return jsonObj;
    }
}

上面的示例代码说明了如何将目标对象的序列化委托回主marshaller。这个的主要优点(除了避免复杂的解决方法)是您仍然可以利用可能已在主上下文中注册的其他类型适配器和自定义序列化程序。请注意,序列化程序和适配器的注册使用完全相同的代码:

// With adapter
final Gson gson = new GsonBuilder().registerTypeAdapter(Pair.class,
        new PairAdapter()).create();

// With serializer
final Gson gson = new GsonBuilder().registerTypeAdapter(Pair.class,
        new PairSerializer()).create();

如果您发现需要坚持使用适配器,那么您可以使用嵌入式Gson代理为您序列化Pair属性,但缺点是您无法访问在父Gson代理上进行的自定义注册:

public class PairAdapter extends TypeAdapter<Pair> {
    final Gson embedded = new Gson();

    public PairAdapter() {
        super();
    }

    @Override
    public void write(final JsonWriter out, final Pair value)
            throws IOException {
        out.beginObject();

        out.name("first");
        embedded.toJson(embedded.toJsonTree(value.getFirst()), out);

        out.name("second");
        embedded.toJson(embedded.toJsonTree(value.getSecond()), out);

        out.endObject();
    }

    @Override
    public Pair read(JsonReader in) throws IOException {
        throw new UnsupportedOperationException();
    }
}

答案 1 :(得分:4)

实际上似乎工作 - 获取对象的适配器 - 使用typeadaptorfactory在对象上发现“json”方法,并让该存储远离适配器对象。对于这样一个明显的用例来说,似乎非常复杂?

例如:

public static @Nullable
TypeAdapter<ImmutableStack<?>> json(final Gson gson)
{
    final TypeAdapter<Object> adaptor = gson.getAdapter(Object.class);
    return new TypeAdapter<ImmutableStack<?>>()
    {
        @Override
        public ImmutableStack<?> read(@Nullable final JsonReader in) throws IOException
        {
            throw ProgramError.notImplementedYet();
        }
        @Override
        public void write(final JsonWriter out, final ImmutableStack<?> value) throws IOException
        {
            out.beginArray();
            for (final Object inner : value)
                adaptor.write(out, inner);
            out.endArray();
        }
    };
}

有人知道更好的方法吗?