在jackson生成的json中包装协议缓冲区时java中的错误?

时间:2012-10-10 00:43:53

标签: java jackson protocol-buffers

我通过执行以下操作发送和接收HTTP post请求:

FooJson fooJson = new FooJson();
fooJson.setName("Bob");

FooProto.Builder fooProto = FooProto.newBuilder(); // google protobuf
fooProto.setColor("Blue");
fooProto.setLength(30);

BarProto.Builder barProto = BarProto.newBuilder();
barProto.setWeight(65);
fooProto.setBarProto(barProto);

barJson.setFooProto(new String(fooProto.build().toByteArray()));
List<BarJson> barJsonList = new ArrayList<BarJson>();
barJsonList.add(barJson);
fooJson.setBarJsonList(barJsonList);
String data = writeJson(fooJson); // wrapper for jackson JsonGenerator
RequestEntity re = new ByteArrayRequestEntity(data.getBytes());

PostMethod method = new PostMethod("http://foo.net:123/path");
method.setRequestEntity(re);
httpClient.executeMethod(method);

在接收端,我解析以下内容:

FooJson fooJson = readJson(request.getInputStream(), FooJson.class);
for (BarJson barJson : fooJson.getBarJsonList()) {
    FooProto fooProto = FooProto.parseFrom(barJson.getFooProto().getBytes());
}

解析接收端协议缓冲区时的结果是:

com.google.protobuf.InvalidProtocolBufferException: While parsing a protocol message, the input ended unexpectedly in the middle of a field.  This could mean either than the input has been truncated or that an embedded message misreported its own length.

我是否在将协议缓冲区转换为字符串的方式出错?我怎么能解决这个问题?

2 个答案:

答案 0 :(得分:3)

我怀疑你可以通过JSON可靠地隧道运行protobuf(其有效载荷是纯二进制,而不是文本),而无需进行某种类型的编码,如base64或十六进制编码。将protobuf有效负载从字节转换为base64文本。然后在接收端将它从base64文本转换回二进制文件。

您将获得protobuf异常,因为接收端的字节数组与您发送的protobuf有效负载不匹配。当您转换为字符串并返回而不使用某种类型的编码时,数据会被释放。

我对javax.xml.bind.DatatypeConverter运气不错。它是Java 1.6的一部分。在发送端使用printBase64Binary,在接收端使用parseBase64Binary。

[更新]

或者,如果base64过于丑陋,您可以使用protobuf-java-format将protobuf对象序列化为多种不同的字符串格式(JSON,XML)。这可能看起来有点奇怪,因为barJson.setFooProto将包含一个本身就是JSON有效负载的字符串。会有很多转义的引号字符 - 但它应该有效。

答案 1 :(得分:0)

你的错误在这里:

barJson.setFooProto(new String(fooProto.build().toByteArray()));

不要尝试从任意二进制数据创建一个字符串 - 这很可能会破坏数据,具体取决于使用的编码;特别是使用JSON,UTF-8编码几乎可以保证损坏(UTF-8中不允许使用某些字节序列,因此您要么获得异常,要么数据会发生变异)。

相反,将字段定义为byte[],让杰克逊使用Base64编码。这是正确的,并且相对有效地完成。

您可能还想考虑仅使用JSON来完成整个事情,如第一个答案所示。无需使用特定的Protobuf风味;如果你真的需要它们,你总是可以单独构造PB对象。