1

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

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

在接收端解析protocol buffer的结果是:

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.

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

4

2 回答 2

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 有效负载。会有很多转义的引号字符 - 但它应该可以工作。

于 2012-10-10T00:50:42.600 回答
0

你的错误在这里:

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

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

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

正如第一个答案所建议的那样,您可能还想考虑只对整个事情使用 JSON。也不需要使用特定的 Protobuf 风味;如果你真的需要它们,你总是可以单独构造 PB 对象。

于 2012-10-12T05:54:36.290 回答