在jackson生成的json中封装协议缓冲区时java出错



我通过以下操作发送和接收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.

我将协议缓冲区转换为字符串的方式有问题吗?我该如何解决这个问题?

我怀疑您是否可以通过JSON可靠地传输protobuf(其有效负载是纯二进制的,而不是文本),而不需要进行base64或十六进制编码之类的编码。将protobuf负载从字节转换为base64文本。然后在接收端将其从base64文本转换回二进制。

您得到的是protobuf异常,因为接收端的字节数组与您发送的protobuf负载不匹配。当您在不使用某种类型的编码的情况下转换为字符串并返回时,数据会被篡改。

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

[更新]

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

您的错误在这里:

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

不要试图从任意二进制数据创建字符串——这很可能会损坏数据,具体取决于使用的编码;特别是对于JSON,UTF-8编码将在很大程度上保证损坏(UTF-8中不允许使用某些字节序列,因此可能会出现异常或数据发生变异)。

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

您可能还想考虑在整个过程中只使用JSON,正如第一个答案所建议的那样。也不需要使用特定的Protobuf风味;如果您确实需要PB对象,那么您总是可以分别构建它们。

最新更新