【问题标题】:Cannot deserialize protobuf data from C++ in Java无法从 Java 中的 C++ 反序列化 protobuf 数据
【发布时间】:2011-08-05 23:01:49
【问题描述】:

我的问题是在 C++ 中序列化 protobuf 数据并在 Java 中反序列化数据。 这是我对dcn给出的提示使用的代码:

有了这个,我在 C++ 中创建了 protobuf 数据并将其写入通过套接字发送的 ostream。

Name name;
name.set_name("platzhirsch");

boost::asio::streambuf b;
std::ostream os(&b);

ZeroCopyOutputStream *raw_output = new OstreamOutputStream(&os);
CodedOutputStream *coded_output = new CodedOutputStream(raw_output);

coded_output->WriteLittleEndian32(name.ByteSize());
name.SerializeToCodedStream(coded_output);
socket.send(b);

这是我尝试解析它的 Java 端:

NameProtos.Name name = NameProtos.Name.parseDelimitedFrom(socket.getInputStream());
System.out.println(name.newBuilder().build().toString());

但是,我得到了这个异常: com.google.protobuf.UninitializedMessageException:消息缺少必填字段:名称

我错过了什么?


有缺陷的代码行是:name.newBuilder().build().toString()

这永远不会奏效,使用未初始化的名称字段创建了一个新实例。无论如何,这里的答案解决了我剩下的问题。

最后一件事,我在 protobuf 邮件列表中被告知:为了刷新 CodedOutputStreams,必须删除对象!

delete coded_output;
delete raw_output;

【问题讨论】:

  • 填充接收到的数组的代码是什么样的?
  • 尝试打印出“message.SerializeAsString()”和“received”的结果,看看它们是否相同。
  • @jtahlborn 我在更新的问题中描述了它。请看一看。
  • @Kannan-Goundan 他们是。另请参阅我修改后的问题。
  • 这不是 \0(字符串结尾)标记?

标签: java c++ protocol-buffers


【解决方案1】:

我不知道您的 Java 代码中的 received 是什么,但您的问题可能是由于某些字符集转换造成的。另请注意,protobuf 在序列化时不会分隔消息。

因此,您应该使用 原始数据 来传输消息(字节数组或直接从/到流(反)序列化)。 如果您打算发送许多消息,您还应该在发送实际消息之前发送大小。

在 Java 中,您可以直接通过 parseDelimitedFrom(InputStream)writeDelimitedTo(OutputStream) 进行操作。你可以在 C++ 中做同样的事情

codedOutput.WriteVarint32(protoMessage.ByteSize());
protoMessage.SerializeToCodedStream(&codedOutput);

另见this ealier thread.

【讨论】:

  • 谢谢,这很有帮助。目前我序列化到 std::ostream 并通过套接字发送。 name.SerializeToOStream(&os);在Java中我这样读: NameProtos.Name.parseDelimitedFrom(socket.getInputStream());但我想如果不使用 CodeOutputStream 它将无法工作?
  • parseDelimitedFrom 期望先读取大小,我认为SerializeToOStream 不会写入大小。如果您只发送一条消息,这没问题(在这种情况下使用 parseFrom 代替)。否则你也必须传输大小。
  • 这太好了,我觉得我在正确的道路上,但是我仍然在 Java 方面遇到异常?你能这么好心,看看我最后编辑的原始问题的附件吗?这是我目前正在使用的代码,我不知道我缺少什么。
【解决方案2】:

您正在向流中写入两件事,一个大小和 Name 对象,但只尝试读取一个。

作为一个普遍的问题:您为什么觉得需要使用 CodedInputStream?引用docs

通常这些类只会 由协议缓冲区内部使用 用于编码和解码的库 协议缓冲区。的客户 图书馆只需要知道这个 如果他们想写自定义类 消息解析或序列化 程序

并强调 jtahlborn 的评论:为什么是小端? Java 处理大端值,因此必须在读取时进行转换。

【讨论】:

  • 这就是我正在做的。我使用 protobuf 进行进程间通信,这意味着,我使用它是消息传递服务。此外,我同意字节序问题,但我找不到 API 文档如何使用替代方法。
  • @platzhirsch - 不确定该评论的含义。但是,如果您将两件事写入流,则需要读取两件事。而且您发布的代码只读取一件事。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
  • 2014-12-30
相关资源
最近更新 更多