2

想在 C# 中创建一个序列化文件,然后想在 Java 中反序列化它。为此,我正在使用协议缓冲区库。我想在这里完成:

在 C# 中,我添加了 protobuf-net.dll 并且要序列化的类表示为:

个人.cs

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

在 main 方法中,将其序列化如下:

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

这个 bin 文件我复制并拉入 ecilpse 文件系统 - android /sdcard 并尝试对其进行反序列化

在日食-JAVA中,

添加了 protobuf.jar、外部库和创建的 person.proto 文件,其中包含:

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

谁能建议如何反序列化在 C# 中创建的对象?

4

3 回答 3

3

Java 文档对反序列化由 Java 版本序列化的对象有什么看法?基本上:“这样做”。序列化的数据没有区别。

如果问题是 Java 示例以 .proto 文件开头,则使用:

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

尽管您显示的 .proto 看起来不错。

于 2013-08-05T17:20:45.913 回答
3

创建一个InputStream在 C# 中创建的文件,然后调用

Person.parseFrom(InputStream)

如果您更愿意处理该文件中的字节,则此方法还有其他重载。

如果你正在实现一个协议,你需要包含一个标头来识别字节代表的数据类型。从那里你只需选择正确的原型来解析数据。

编辑

这是我创建的一个类,用于将 id 映射到类,反之亦然,以帮助开发带有 protobufs 的协议。如果您不开发网络传输协议,这可能无济于事,但我很确定您是。

我知道你没有要求这个,但也许你会发现它很有用。

将 id 注册到所有 protobuff 生成器,然后检索正确的生成器以在接收时取消序列化字节。在发送之前为每个 protobuf 对象获取正确的 ID。ID 将包含在每个数据包中,以便您知道每个数据包中的数据类型。(这里的数据包是抽象的,这也适用于流协议。)

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

}

用法:

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());
于 2013-08-05T17:31:21.573 回答
2

协议缓冲区的工作方式通常是:

  • 您在一个小.proto文本文件中定义您的协议。
  • 您在该文件上运行 protobuf 编译器
  • proto 编译器生成序列化和解析对象所需的所有语言的源代码。

您在 C# 中使用的库是特殊的。它不需要该.proto文件开始。相反,您可以通过注释类(或 C# 中的任何术语)在代码中创建协议。

为了使用 Java 代码内生成的协议,您需要该.proto文件,因为 Java 不支持代码中的这种类型的定义。

您可以.proto手动编写文件,也可以让 C# 库生成它(请参阅 Marc Gravell 的回答) - 我建议您生成文件,这样您就不会在定义中出错。

获得该文件后,您在该文件上运行 protobuf 编译器 ( ) protoc( dowload).proto,它将.java为您生成一个文件。该文件具有Person序列化和解析所需的类和所有内容。

现在您在项目中包含这两个文件protobuf[version].jar和生成的.java文件。该.proto文件本身在项目中不是必需的。

完成后,只需使用生成的代码来解析文件:

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

您编写的唯一一行代码就是上面的那一行。

有关 Java 集成的更多详细信息,请参见官方教程

于 2013-08-05T19:46:08.243 回答