如果输入是字符串,gson 似乎会按原样返回字符串。它不对输入进行任何验证。这是预期的吗?
是的,这很好。它只返回给定字符串的 JSON 字符串表示形式。
我想以一种可以验证输入对象实际上是 Json 的方式使用 Gson。我怎么能这样做?
本身不需要。 Gson.toJson() 方法接受要序列化的对象并始终生成有效的 JSON。如果你的意思是反序列化,那么 Gson 在读取/解析/反序列化(实际上是读取,这是 Gson 的最底层)期间对无效的 JSON 文档进行快速失败。
我将在短时间内调用这个序列化函数数千次。转换为 String 然后转换为 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 是否以某种方式公开/接受OutputStreams——然后您可以轻松地将此类输出流传递给serializeToBytes 方法,甚至删除该方法。如果它可以使用输入流,而不仅仅是字节数组,您还可以查看converting output streams to input streams,以便serializeToBytes 方法可以返回InputStream 或Reader(需要一些开销,但可以处理无限数据 - - 需要找到平衡点):
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"。