2011-09-27 50 views
1

一个potobuf-net'ized文件我正在写一个程序,保存文件“映射”到HD这样我可以在以后打开它们,并显示相同的数据。我的地图最初保存只有一个数据类型,一组具有的属性我自己的自定义对象:idlayerxy。你可以看到我做的代码,这里:保存多种文件类型为在VB.net

<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,A级,Y,并Value
  • NameValue
  • Name,A级,ENUMVALUEXYINTEGERVALUE,和几个其他的事情(这一个必须包含相当多的数据)。

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

回答

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之间没有区别 - 或者将导致SomeNameFoo0Foo1,AnotherNameBar0。此选项与您现有的数据兼容,因为内部序列化“Foo列表”与“包含Foo列表的字段1的包装类”之间没有区别。

第二个选项是模仿上述情况,但是在反序列化过程中使用手动类型检查 - 这涉及使用非通用API并提供将字段编号(1,2,3)映射到类型(Foo)的委托,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/Bar/Blap。这很简单方便,并且很好地保留了订单;但它不是相当兼容数据序列化只是作为List<Foo> - 如果现有的序列化数据是一个问题,您将需要迁移格式之间。

+0

@OP你再次救了我先生Gravell先生!我只是实现了你提供的Wrapper方法,没有一个单一的顺利!万分感谢! – FreeSnow

1

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

例如,这里是我会怎么写和读StrippedElement:如果你喜欢写那些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 

所以,只要写下元素(INT32,4字节)的数量就知道下次要从文件中读取多少。

+0

除非您完全知道*您正在做什么,并且需要它*绝对紧密*,但序列化是IMO最好留给不会产生令人讨厌的细微错误的工具(例如,“读取器”(BinaryReader ')声明为方法的本地可能会轻易过度使用流缓冲区... BOOM!)。这不是大多数人需要关心的代码。另外:考虑“endianness”(可能在单声道上运行)。最后,为什么在最后的例子中,'SE'(已知是一个类)声明了'ByRef'? –

+0

因为我需要那个SE以外的SE,所以它是一个全局变量.. Dim SE as StrippedElement'It is Empty ReadStrippedElement(MyFileStream,SE) Dim x as integer = SE.X'现在SE包含数据 – Miharbicha

+0

不会改变任何事......这是一堂课;只要你设置了'SE.X',你就更新了对象。如果您将参数值重新分配给新实例,并且需要调用程序可以看到* assignment *(即SE = new StrippedElement();'(或VB等价物),则只需要'ByRef' 。你的代码没有赋值(并且'Function'可能会更容易) –

相关问题