Cannot deserialize protobuf data from C++ in Java

2019-04-11 13:39发布

问题:

My problem is to serialize protobuf data in C++ and deserialize the data in Java probably. Here is the code I use to the hints given by dcn:

With this I create the protobuf data in C++ and write it to an ostream which is send via socket.

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

This is the Java side where I try to parse it:

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

However by this I get this Exception: com.google.protobuf.UninitializedMessageException: Message missing required fields: name

What am I missing?


The flawed code line is: name.newBuilder().build().toString()

This would have never worked, a new instance is created with uninitialized name field. Anyway the answer here solved the rest of my problem.

One last thing, which I was told in the protobuf mailinglist: In order to flush the CodedOutputStreams, the objects have to be deleted!

delete coded_output;
delete raw_output;

回答1:

I don't know what received is in your Java code, but your problem may be due to some charset conversion. Note also that protobuf does not delimit the messages when serializing.

Therefore you should use raw data to transmit the messages (byte array or directly (de)serialize from/to streams). If you intent to send many message you should also send the size before you send the actual messages.

In Java you can do it directly via parseDelimitedFrom(InputStream) and writeDelimitedTo(OutputStream). You can do the same in C++ a litte more complex using CodedOutputStream like

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

See also this ealier thread.



回答2:

You're writing two things to the stream, a size and the Name object, but only trying to read one.

As a general question: why do you feel the need to use CodedInputStream? To quote the docs:

Typically these classes will only be used internally by the protocol buffer library in order to encode and decode protocol buffers. Clients of the library only need to know about this class if they wish to write custom message parsing or serialization procedures

And to emphasize jtahlborn's comment: why little-endian? Java deals with big-endian values, so will have to convert on reading.