繁体   English   中英

将对象序列化为Json

[英]Serializing Object to Json

我正在尝试使用Gson来获取一些Java Object并将其序列化为json并获取表示该Json的字节数组。 我需要一个字节数组,因为我将输出传递给需要它为字节数组的外部依赖项。

public byte[] serialize(Object object){
  return gson.toJson(object).getBytes();
}

我有两个问题:

  1. 如果输入是String,则gson似乎会按原样返回String。 它不对输入进行任何验证。 这是预期的吗? 我想以一种可以验证输入对象实际上是Json的方式使用Gson。 我该怎么办?
  2. 我要在短时间内调用该序列化函数数千次。 转换为String然后转换为byte[]可能会带来一些不必要的开销。 是否有更优化的方法来获取byte[]

编辑:我对第1点的回答是错误的。

2)如果您仅使用普通gson转换器,则会有很多不必要的反射开销。 在您的情况下,编写自定义适配器将极大地提高性能。 这是一篇关于该内容的更多信息的文章https://open.blogs.nytimes.com/2016/02/11/improving-startup-time-in-the-nytimes-android-app/?_r=0

如果输入是String,则gson似乎会按原样返回String。 它不对输入进行任何验证。 这是预期的吗?

是的,这很好。 它只是返回给定字符串的JSON字符串表示形式。

我想以一种可以验证输入对象实际上是Json的方式使用Gson。 我该怎么办?

本身就不需要。 Gson.toJson()方法接受要序列化的对象,并始终生成有效的JSON。 如果您要进行反序列化,则Gson会在读取/解析/反序列化过程中对无效的JSON文档进行快速失败(实际上是读取,这是Gson的最底层)。

我要在短时间内调用该序列化函数数千次。 转换为String然后转换为byte []可能会带来一些不必要的开销。 是否有更优化的方法来获取byte []?

是的,仅为了公开其内部char[]克隆而累积JSON字符串会浪费内存。 Gson基本上是一种面向流的工具,请注意,有一些Gson.toJson方法重载接受了Appendable ,而这些重载基本上是Gson核心(只是快速了解Gson.fromJson(Object)工作方式-它只是创建一个StringWriter实例由于Appendable接口而累积了一个字符串)。 如果Gson可以通过Reader而不是写入Appendable发出JSON令牌,那将非常酷,但是很遗憾,这个想法被拒绝了,很可能永远不会在Gson中实现。 由于Gson在反序列化期间不会以读取语义的方式(从您的代码角度)发出JSON令牌,因此您必须缓冲整个结果:

private static byte[] serializeToBytes(final Object object)
        throws IOException {
    final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    final OutputStreamWriter writer = new OutputStreamWriter(outputStream);
    gson.toJson(object, writer);
    writer.flush();
    return outputStream.toByteArray();
}

这个不使用StringWriter因此不会累积带有克隆数组ping-pong的中间字符串。 我不知道是否有可以利用/重用现有字节数组的编写器/输出流,但是我相信应该有一些,因为它为您在问题中提到的性能目的提供了很好的理由。

如果可能的话,您还可以检查库接口/ API是否以某种方式公开/接受OutputStream那么您可以轻松地将此类输出流传递给serializeToBytes方法,甚至删除该方法。 如果它可以使用输入流,而不仅仅是字节数组,那么您还可以看一下将输出流转换为输入流,以便serializeToBytes方法可以返回InputStreamReader (需要一些开销,但可以处理无限数据-找到余额):

private static InputStream serializeToByteStream(final Object object)
        throws IOException {
    final PipedInputStream inputStream = new PipedInputStream();
    final OutputStream outputStream = new PipedOutputStream(inputStream);
    new Thread(() -> {
        try {
            final OutputStreamWriter writer = new OutputStreamWriter(outputStream);
            gson.toJson(object, writer);
            writer.flush();
        } catch ( final IOException ex ) {
            throw new RuntimeException(ex);
        } finally {
            try {
                outputStream.close();
            } catch ( final IOException ex ) {
                throw new RuntimeException(ex);
            }
        }
    }).start();
    return inputStream;
}

使用示例:

final String value = "foo";
System.out.println(Arrays.toString(serializeToBytes(value)));
try ( final InputStream inputStream = serializeToByteStream(value) ) {
    int b;
    while ( (b = inputStream.read()) != -1 ) {
        System.out.print(b);
        System.out.print(' ');
    }
    System.out.println();
}

输出:

[34,102,111,111,34]
34102111111 34

两者都代表一个ASCII码数组,从字面上表示一个字符串"foo"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM