简体   繁体   English

将对象序列化为Json

[英]Serializing Object to Json

I am attempting to use Gson to to take some Java Object and serialize that to json and get a byte array that represents that Json. 我正在尝试使用Gson来获取一些Java Object并将其序列化为json并获取表示该Json的字节数组。 I need a byte array because I am passing on the output to an external dependency that requires it to be a byte array. 我需要一个字节数组,因为我将输出传递给需要它为字节数组的外部依赖项。

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

I have 2 questions: 我有两个问题:

  1. If the input is a String gson seems to return the String as is. 如果输入是String,则gson似乎会按原样返回String。 It doesn't do any validation of the input. 它不对输入进行任何验证。 Is this expected? 这是预期的吗? I'd like to use Gson in a way that it would validate that the input object is actually Json. 我想以一种可以验证输入对象实际上是Json的方式使用Gson。 How could I do this? 我该怎么办?
  2. I'm gonna be invoking this serialize function several thousands of times over a short period. 我要在短时间内调用该序列化函数数千次。 Converting to String and then to byte[] could be some unwanted overhead. 转换为String然后转换为byte[]可能会带来一些不必要的开销。 Is there a more optimal way to get the byte[] ? 是否有更优化的方法来获取byte[]

edit: my answer on point 1 was misinformed. 编辑:我对第1点的回答是错误的。

2) There will be a lot of unnecessary overhead in reflection if you just use the vanilla gson converter. 2)如果您仅使用普通gson转换器,则会有很多不必要的反射开销。 It would very much be a performance benefit in your case to write a custom adapter. 在您的情况下,编写自定义适配器将极大地提高性能。 here is one article with more info on that https://open.blogs.nytimes.com/2016/02/11/improving-startup-time-in-the-nytimes-android-app/?_r=0 这是一篇关于该内容的更多信息的文章https://open.blogs.nytimes.com/2016/02/11/improving-startup-time-in-the-nytimes-android-app/?_r=0

If the input is a String gson seems to return the String as is. 如果输入是String,则gson似乎会按原样返回String。 It doesn't do any validation of the input. 它不对输入进行任何验证。 Is this expected? 这是预期的吗?

Yes, this is fine. 是的,这很好。 It just returns a JSON string representation of the given string. 它只是返回给定字符串的JSON字符串表示形式。

I'd like to use Gson in a way that it would validate that the input object is actually Json. 我想以一种可以验证输入对象实际上是Json的方式使用Gson。 How could I do this? 我该怎么办?

No need per se. 本身就不需要。 Gson.toJson() method accepts objects to be serialized and it generates valid JSON always. Gson.toJson()方法接受要序列化的对象,并始终生成有效的JSON。 If you mean deserialization, then Gson makes fast fails on invalid JSON documents during reading/parsing/deserialization (actually reading, this is the bottom-most layer of Gson). 如果您要进行反序列化,则Gson会在读取/解析/反序列化过程中对无效的JSON文档进行快速失败(实际上是读取,这是Gson的最底层)。

I'm gonna be invoking this serialize function several thousands of times over a short period. 我要在短时间内调用该序列化函数数千次。 Converting to String and then to byte[] could be some unwanted overhead. 转换为String然后转换为byte []可能会带来一些不必要的开销。 Is there a more optimal way to get the byte[]? 是否有更优化的方法来获取byte []?

Yes, accumulating a JSON string to in order just to expose its internal char[] clone is memory waste, of course. 是的,仅为了公开其内部char[]克隆而累积JSON字符串会浪费内存。 Gson is basically a stream-oriented tool, and note that there are Gson.toJson method overloads accepting Appendable that are basically the Gson core (just take a quick look on how Gson.fromJson(Object) works -- it just creates a StringWriter instance to accumulate a string because of the Appendable interface). Gson基本上是一种面向流的工具,请注意,有一些Gson.toJson方法重载接受了Appendable ,而这些重载基本上是Gson核心(只是快速了解Gson.fromJson(Object)工作方式-它只是创建一个StringWriter实例由于Appendable接口而累积了一个字符串)。 It would be extremely cool if Gson could emit JSON tokens via a Reader rather than writing to an Appendable , but this idea was refused and most likely will never be implemented in Gson, unfortunately. 如果Gson可以通过Reader而不是写入Appendable发出JSON令牌,那将非常酷,但是很遗憾,这个想法被拒绝了,很可能永远不会在Gson中实现。 Since Gson does not emit JSON tokens during deserialization in read semantics manner (from your code perspective), you have to buffer the whole result: 由于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();
}

This one does not use StringWriter thus not accumulating an intermediate string with cloned arrays ping-pong. 这个不使用StringWriter因此不会累积带有克隆数组ping-pong的中间字符串。 I don't know if there are writers/output streams that can utilize/re-use existing byte arrays, but I believe there should be some, because it makes a good rationale for the performance purposes you mentioned in your question. 我不知道是否有可以利用/重用现有字节数组的编写器/输出流,但是我相信应该有一些,因为它为您在问题中提到的性能目的提供了很好的理由。

If possible, you can also check your library interface/API for exposing/accepting OutputStream s somehow -- then you could probably easily pass such output streams to the serializeToBytes method or even remove the method. 如果可能的话,您还可以检查库接口/ API是否以某种方式公开/接受OutputStream那么您可以轻松地将此类输出流传递给serializeToBytes方法,甚至删除该方法。 If it can use input streams, not just byte arrays, you could also take a look at converting output streams to input streams so the serializeToBytes method could return an InputStream or a Reader (requires some overhead, but can process infinite data -- need to find the balance): 如果它可以使用输入流,而不仅仅是字节数组,那么您还可以看一下将输出流转换为输入流,以便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;
}

Example of use: 使用示例:

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();
}

Output: 输出:

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

Both represent an array of ASCII codes representing a string "foo" literally. 两者都代表一个ASCII码数组,从字面上表示一个字符串"foo"

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

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