将多种文件类型保存到VB.net中的一个potobuf-net'ized文件中

时间:2011-09-27 10:39:26

标签: vb.net types protobuf-net save

我正在编写一个程序,将“地图”文件保存到HD,以便我以后可以打开它们并显示相同的数据。我的地图最初只保存了一种数据类型,一组我自己的自定义对象,其中包含以下属性:idlayerxy。您可以在此处查看我为此所做的代码:

<ProtoContract()> _
Public Class StrippedElement
    <ProtoMember(1)> _
    Public Property X() As Integer
        Get
            Return m_X
        End Get
        Set(ByVal value As Integer)
            m_X = value
        End Set
    End Property
    Private m_X As Integer
    <ProtoMember(2)> _
    Public Property Y() As Integer
        Get
            Return m_Y
        End Get
        Set(ByVal value As Integer)
            m_Y = value
        End Set
    End Property
    Private m_Y As Integer
    <ProtoMember(3)> _
    Public Property Id() As Long
        Get
            Return m_Id
        End Get
        Set(ByVal value As Long)
            m_Id = value
        End Set
    End Property
    Private m_Id As Long
    <ProtoMember(4)> _
    Public Property Layer() As String
        Get
            Return m_Layer
        End Get
        Set(ByVal value As String)
            m_Layer = value
        End Set
    End Property
    Private m_Layer As String
End Class

基本上,我只是将吨和吨的这些类序列化为一个文件。现在我发现我必须保存地图的新部分,这些部分不一定是同一类类型。

是否可以将多种类型保存到同一个文件中,并且仍可以简单地从中读取?这是我在文件中写入和读取的代码:

    Public Shared Sub Save(ByVal File As String, ByVal Map As RedSimEngine.Map)
        Dim nPBL As New List(Of StrippedElement)
        For z As Integer = 0 To Grid.LAYERLIMIT
            For x As Integer = 0 To Grid.GRIDLIMIT
                For y As Integer = 0 To Grid.GRIDLIMIT
                    Dim currentCell As GridElement = Map.Level.getCell(z, x, y)
                    If currentCell IsNot Nothing Then
                        If currentCell.Archivable Then
                            Dim nStEl As New StrippedElement
                            nStEl.Id = currentCell.getId()
                            nStEl.Layer = currentCell.getLayer()
                            nStEl.X = currentCell.X
                            nStEl.Y = currentCell.Y
                            nPBL.Add(nStEl)
                        End If
                    End If
                Next
            Next
        Next
        Serializer.Serialize(New FileStream(File, FileMode.Create), nPBL)
    End Sub

    Public Shared Function Load(ByVal File As String) As RedSimEngine.Map
        Dim nMap As New Map
        Dim nListOfSE As List(Of StrippedElement) = Serializer.Deserialize(Of List(Of StrippedElement))(New FileStream(File, FileMode.Open))
        For Each elm As StrippedElement In nListOfSE
            Dim nElm As GridElement = GridElement.createElementByIdAndLayer(elm.Layer, elm.Id)
            nElm.X = elm.X
            nElm.Y = elm.Y
            nMap.Level.setCell(nElm)
        Next
        Return nMap
    End Function

我必须在保存文件中添加3个或更多类类型,我宁愿不将它分开,因为这会让我的客户感到困惑。

基本上,我必须添加类似于以下内容的东西:

  • X Y Value
  • 的课程
  • Name Value
  • 的课程
  • Name ENUMVALUE X {{ 1}} Y ,以及其他一些事情(这个必须包含相当多的数据)。

我正在使用VB.net,因此所有.net答案都可以接受。谢谢!如果您需要任何澄清,请在评论中说明。

2 个答案:

答案 0 :(得分:1)

这里有3个选项:

第一个选项是编写一个包含3个容器的包装类:

 [ProtoContract] public class MyData {
     [ProtoMember(1)] public List<Foo> SomeName {get;set;} // x,y,value
     [ProtoMember(2)] public List<Bar> AnotherName {get;set;} // name,value
     [ProtoMember(3)] public List<Blap> ThirdName {get;set;} // etc
 }

并序列化该实例;但请注意,此处的订单将会丢失 - 即在反序列化后Foo0, Bar0, Foo1Foo0, Foo1, Bar0之间没有区别 - 要么SomeName导致Foo0和{{ 1}}和Foo1AnotherName。此选项与您现有的数据兼容,因为内部将“Foo列表”与“Foo列表为包含字段1的包装类”序列化没有区别。

第二个选项是模仿上述内容,但在反序列化期间使用手动类型检查 - 这涉及使用非泛型API并提供将字段编号(1,2,3)映射到类型({{ 1}},Bar0Foo)。这对于非常大的流非常有用,或者选择性地从流中提取对象,因为它允许您单独处理单个对象(或忽略它们)。使用这种方法,您还可以累积构建文件,而不是构建整个列表。但是,这个例子有点复杂,所以除非感兴趣,否则我宁愿不添加一个。此方法与您现有的数据兼容。

第三种方法是继承,即

Bar

然后序列化Blap碰巧包含 [ProtoContract, ProtoInclude(1, typeof(Foo))] [ProtoInclude(2, typeof(Bar)), ProtoInclude(3, typeof(Blap))] public class SomeBaseType {} [ProtoContract] public class Foo : SomeBaseType { /* properties etc*/ } [ProtoContract] public class Bar: SomeBaseType { /* properties etc*/ } [ProtoContract] public class Blap: SomeBaseType { /* properties etc*/ } / List<SomeBaseType> / Foo的实例。这很简单方便,保存顺序很好;但它与<{1}}序列化的数据完全兼容 - 如果现有的序列化数据存在问题,则需要在格式之间进行迁移。

答案 1 :(得分:1)

我发现“Serializer.Serialize”在这种情况下非常不洁净,所以这是我将如何进行: 我会一次手动编写变量!

例如,以下是我将如何编写和读取StrippedElement:

    Sub WriteStrippedElement(ByVal Stream As IO.Stream, ByVal SE As StrippedElement)
    Stream.Write(BitConverter.GetBytes(SE.X), 0, 4) 'Write X:integer (4 bytes)
    Stream.Write(BitConverter.GetBytes(SE.Y), 0, 4) 'Write Y:integer (4 bytes)
    Stream.Write(BitConverter.GetBytes(SE.Id), 0, 8) 'Write Id:Long (8 bytes)
    Dim LayerBuffer() As Byte = System.Text.Encoding.Default.GetBytes(SE.Layer) 'Converting String To Bytes
    Stream.Write(BitConverter.GetBytes(LayerBuffer.Length), 0, 4) 'Write The length of layer, since it can't have a fixed size:integer (4 bytes)
    Stream.Write(LayerBuffer, 0, LayerBuffer.Length) 'Write The Layer Data
    Stream.Flush() 'We're Done :)
End Sub
Sub ReadStrippedElement(ByVal Stream As IO.Stream, ByRef SE As StrippedElement)
    Dim BinRead As New IO.BinaryReader(Stream) 'Making reading Easier, We can also use a BinaryWriter in the WriteStrippedElement For example
    SE.X = BinRead.ReadInt32 'Read 4 Bytes
    SE.Y = BinRead.ReadInt32 'Read 4 Bytes
    SE.Id = BinRead.ReadInt64 'Read 8 Bytes
    Dim Length As Integer = BinRead.ReadInt32 'Read 4 Bytes, the length of Layer
    Dim LayerBuffer() As Byte = BinRead.ReadBytes(Length) 'Read Layer Bytes
    SE.Layer = System.Text.Encoding.Default.GetString(LayerBuffer) 'Convert Back To String, and done.
End Sub

因此,如果你想编写很多那些StrippedElement,只需记下元素的数量(int32,4bytes),以便知道下次从文件中读取多少内容。