2

Want to create a serialized file in C# and then want to de-serialize the same in Java. For this , i am using Protocol buffer library. Here want i have done:

In C#, I added protobuf-net.dll and the class which is to be serialized is represented as:

Person.cs

 [ProtoBuf.ProtoContract]
 public class Person
 {
    [ProtoBuf.ProtoMember(1)]
    public int Id {get;set;}
    [ProtoBuf.ProtoMember(2)]
    public string Name { get; set; }
 }

and in the main method, serialized it like this:

 var person = new Person
    {
        Id = 12345,
        Name = "Fred",
    };
  using (var file = File.Create("somepath\\person.bin"))
    {
        Serializer.Serialize(file, person);
    }

This bin file i copied and pulled in the ecilpse filesystem- android /sdcard and tried to de-serialize it

In eclipse -JAVA,

Added protobuf.jar, external library and created person.proto file which contains:

message Person{
required int32 Id=1;
required string Name=2;
}

Can anyone please suggest how to de-serialize the object which was created in the C#?

Community
  • 1
  • 1
Aada
  • 1,591
  • 7
  • 30
  • 57
  • 1
    have a look at the Java tutorial https://developers.google.com/protocol-buffers/docs/javatutorial you also need to use the same `.proto` to generate the code that serializes in C# / parses in Java. – zapl Aug 05 '13 at 17:26
  • Hi @zapl: same .proto !! I guess i am doing the same. person.proto is the file. Is am i correct? – Aada Aug 05 '13 at 17:48
  • That's correct. Its one of the strengths of protobufs. Don't need to write protocol routines twice. That's what the `.proto` files are for. – William Morrison Aug 05 '13 at 17:50
  • Hi @WilliamMorrison: So, can't we use the same way for de-serialization,to read the object back- new ObjectInputStream(new FileInputStream(f)).readObject();? – Aada Aug 05 '13 at 18:03
  • No. That makes use of Java's built-in serialization. You are using a whole other library for serialization/deserialization now. – William Morrison Aug 05 '13 at 18:05

3 Answers3

3

What does the Java documentation say about deserializing an object serialized by the Java version? Basically: "do that". There is no difference in the serialized data.

If the problem is that the Java examples start with a .proto file, then use:

string proto = Serializer.GetProto<Person>();

Although the .proto you show looks fine.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    He asked how to unserialize the object. This is a serialization example. – William Morrison Aug 05 '13 at 17:23
  • @WilliamMorrison actually, no it is explicitly **not** a serialization example – Marc Gravell Aug 05 '13 at 18:56
  • I must be confused... `string proto = Serializer.GetProto();' looks like its serializing something into a string to me. I'm not familiar with the C# version, but let me say thank you ahead of time. I'm positive I'll be using your software in the future! – William Morrison Aug 05 '13 at 19:08
  • My mistake, I see what you're saying here. Compare the .proto files, see if there's a difference. I apologize. Again, great work. – William Morrison Aug 05 '13 at 19:21
  • @William no - GetProto returns the .proto schema definition to feed into the tools (protoc etc) for starting off the Java code. If it were an xml serializer, it would be `GetXsd()`, if you see what I mean – Marc Gravell Aug 05 '13 at 19:40
  • Oh, think I get it now. Generate the proto definition from the attributes set on fields in C#. Feed that definition into the tools to generate java code. I got you. You suggest this because you can't generate C# code from proto definitions in protobuf-net right? – William Morrison Aug 05 '13 at 19:58
  • Hi Marc,I am facing an error: Possible recursion detected... As you discussed in this post: http://stackoverflow.com/questions/6127380/exception-while-serializing-a-graph ;; from where i can find the source/dll – Aada Aug 06 '13 at 12:37
  • @Aada in order for you to get an error, *any error*, you must already have the dll. So the question is: is your type recursive? There's a very good chance that the message is correct. Note protobuf-net does include some support for working around cycles, but it depends on what your model looks like. – Marc Gravell Aug 06 '13 at 12:41
3

Create an InputStream to the file created in C#, and call

Person.parseFrom(InputStream)

There are other overloads to this method if you'd rather deal with bytes from that file.

If you are implementing a protocol you'll need to include a header to identify what type of data the bytes represent. From there you'd just select the correct proto to parse the data with.

EDIT

Here's a class I created for mapping id's to class's and vice versa to assist in developing a protocol with protobufs. If you aren't developing a protocol for network transmission this might not help, but I'm pretty sure you are.

I know you didn't ask for this, but perhaps you'll find it useful.

Register ids to all your protobuff generators, then retrieve the correct generator for unserializing bytes on receive. Get the correct ID for each protobuf object before you send. ID would be included in every packet so you know what type of data is in each packet. (Packet being abstract here, this would work with a stream protocol too.)

public class MessageTypeMap {
private final Object lock;
final HashMap<Integer, GeneratedMessageLite> messageParserMap;
final HashMap<Class<?>, Integer> messageClassParserMap;

public MessageTypeMap() {
    this.messageParserMap = new HashMap<Integer, GeneratedMessageLite>();
    this.messageClassParserMap = new HashMap<Class<?>, Integer>();
    this.lock = new Object();
}

public void addMessageType(int typeID, GeneratedMessageLite message) {
    synchronized (this.lock) {
        this.messageParserMap.put(typeID, message);
        this.messageClassParserMap.put(message.getDefaultInstanceForType()
                .getClass(), typeID);
    }
}

public GeneratedMessageLite getBuilderFor(int id) throws ProtocolException {
    synchronized (this.lock) {
        if (this.messageParserMap.containsKey(id)) {
            GeneratedMessageLite lite = this.messageParserMap.get(id);
            return lite;
        } else {
            throw new ProtocolException("No message builder for ID " + id);
        }
    }
}

public int getIDFor(Object obj) throws ProtocolException {
    synchronized (this.lock) {
        if (obj == null) {
            throw new NullPointerException(
                    "Object null while retrieving type id.");
        }
        Class<?> c = obj.getClass();
        if (this.messageClassParserMap.containsKey(c)) {
            int typeID = this.messageClassParserMap.get(c);
            return typeID;
        } else {
            throw new ProtocolException("No type id for class "
                    + c.getSimpleName());
        }
    }
}

}

Usage:

MessageTypeMap map = new MessageTypeMap();
//register the person type.
map.addMessageType(100, Person.getDefaultInstance());
//use this to unserialize whatever object some bytes are.
GeneratedMessageLite builder = mpa.getBuilderFor(100);
//be sure to include the type id with each transmission of an object.
int id = map.getIDFor(Person.getDefaultInstance());
William Morrison
  • 10,953
  • 2
  • 31
  • 48
  • Hi William can you please explain me, where are you reading it back? – Aada Aug 05 '13 at 18:25
  • `Person.parseFrom(new InputStream(pathToFile));` is how you read. Its at the very top of my question. The rest is just code to help you read back bytes during network operations. – William Morrison Aug 05 '13 at 19:27
2

The usual way how Protocol Buffers work is:

  • You define your protocol in a small .proto text file.
  • You run the protobuf compiler on that file
  • proto compiler generates source code in all the languages you need to serialize & parse objects.

The library you use in C# is special. It does not need that .proto file to start with. Instead you can create the protocol within your code by annotating classes (or whatever the terminology in C# is).

In order to use your in-code-generated protocol from Java you need the .proto file since Java does not support that type of definition in code.

You can either write the .proto file by hand or let the C# library generate it (see Marc Gravell's answer) - I would suggest you generate the file so you can't make mistakes in the definition.

Once you have that file you run the protobuf compiler (protoc) (dowload) on the .proto file and it will generate a .java file for you. That file has the Person class and everything required to serialize and parse.

Now you include both protobuf[version].jar and the generated .java file in your project. The .proto file itself is not required in the project.

Once you have done that simply use the generated code to parse the file:

Person person = Person.parseFrom(new FileInputStream(file));

The only line of code that you write is that one above.

More details about the Java integration is found in the official tutorial

zapl
  • 63,179
  • 10
  • 123
  • 154