将对象序列化为Json

时间:2017-03-30 17:32:24

标签: java gson

我试图使用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[]是否有更优化的方法?

2 个答案:

答案 0 :(得分:0)

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

2)如果你只是使用香草gson转换器,反射会有很多不必要的开销。在您的情况下编写自定义适配器非常有性能优势。这是一篇文章,其中包含更多信息 https://open.blogs.nytimes.com/2016/02/11/improving-startup-time-in-the-nytimes-android-app/?_r=0

答案 1 :(得分:0)

  

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

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

  

我想以一种方式使用Gson,它会验证输入对象实际上是Json。我怎么能这样做?

无需本身。 Gson.toJson()方法接受要序列化的对象,并始终生成有效的JSON。如果你的意思是反序列化,那么Gson会在读取/解析/反序列化期间对无效的JSON文档进行快速失败(实际读取时,这是Gson的最底层)。

  

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

是的,当然,为了暴露其内部char[]克隆而累积JSON字符串是一种内存浪费。 Gson基本上是一个面向流的工具,请注意有Gson.toJson方法重载接受Appendable基本上是Gson核心(只需快速了解Gson.fromJson(Object)如何工作 - 它只需创建一个StringWriter实例来累积字符串,因为Appendable接口)。如果Gson可以通过Reader发出JSON令牌而不是写入Appendable,那将是非常酷的,但是这个idea被拒绝了,很可能永远不会在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因此不会累积带有克隆数组乒乓的中间字符串。我不知道是否有可以利用/重用现有字节数组的编写器/输出流,但我相信应该有一些,因为它为你在问题中提到的性能目的提供了一个很好的理由。

如果可能,您还可以检查库接口/ API以某种方式公开/接受OutputStream - 然后您可以轻松地将此类输出流传递给serializeToBytes方法,甚至可以删除方法。如果它可以使用输入流,而不仅仅是字节数组,您还可以查看converting output streams to input streams,以便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]
  34 102 111 111 34

两者都代表一个ASCII代码数组,字面意思是字符串"foo"