如何使用协议缓冲区在Java中反序列化文件

本文关键字:反序列化 文件 Java 何使用 协议 缓冲区 | 更新日期: 2023-09-27 18:26:27

希望在C#中创建一个序列化文件,然后在Java中对其进行反序列化。为此,我使用协议缓冲库。这里想要我做的:

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

Person.cs

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

在主要方法中,将其序列化为:

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

我复制并拉入ecilpse文件系统android/sdcard中的这个bin文件,并试图将其反序列化

在eclipse-JAVA中,

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

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

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

如何使用协议缓冲区在Java中反序列化文件

Java文档对反序列化Java版本序列化的对象有何说明?基本上:"那样做"。序列化的数据没有差异。

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

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

尽管你展示的.proto看起来不错。

为C#中创建的文件创建一个InputStream,并调用

Person.parseFrom(InputStream)

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

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

编辑

这是我创建的一个类,用于将id映射到类,反之亦然,以帮助开发带有protobufs的协议。如果你没有开发一个网络传输协议,这可能没有帮助,但我敢肯定你是。

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

向所有protobuff生成器注册id,然后在接收时检索正确的生成器以取消字节序列化。在发送之前,请获取每个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());

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

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

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

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

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

一旦你有了这个文件,你就在.proto文件上运行protobuf编译器(protoc)(dowload),它会为你生成一个.java文件。该文件包含Person类以及序列化和解析所需的所有内容。

现在,您的项目中包含了protobuf[version].jar和生成的.java文件。项目中不需要.proto文件本身。

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

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

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

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