65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义类集合序列化和反序列化的完整示例

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2014年6月19日

CPOL

2分钟阅读

viewsIcon

22761

downloadIcon

312

如果您想了解如何序列化和反序列化自定义类集合,这个技巧适合您。

引言

net 上大多数序列化和反序列化的示例都是为 C# 编写的,并使用 List<T> 通用类。

本技巧提供了一种序列化和反序列化从 CollectionBase 继承的自定义类的方法,并解释了以下问题的根源

  • 在反射类型 'xxx' 时发生错误。 (System.InvalidOperationException)
  • 您必须在 'xxx' 上实现一个默认访问器,因为它继承自 ICollection。 (System.InvalidOperationException)

背景

我假设您已经具备以下基本知识

  1. 如何编写自定义类
  2. 如何创建类集合
  3. 如何使用嵌套类
  4. 什么是序列化和反序列化

如果您想完善您的知识,这里有一系列有用的链接

  1. MSDN
  2. CodeProject 文章/技巧

案例研究

假设您需要编写一个应用程序,该应用程序

  1. 可以收集笔记,并且每条笔记都使用自定义 id
  2. 提供将数据导出到 XML 文件的方法
  3. 提供从 XML 文件导入数据的方法

还有另一个要求:XML 文件必须满足模式要求 (*.xsd 文件)

<!--?xml version="1.0"?-->
<xs:schema targetnamespace="urn:SoftwareDeveloperName" 
xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:annotation>
    <xs:documentation>
      -- Copyright (C) 2014 (author: Your Name here). --
    </xs:documentation>
  </xs:annotation>
  <xs:element name="MyData">
    <xs:complextype>
      <xs:sequence>
        <xs:element type="xs:string" name="Version" fixed="1.0">
        <xs:element name="Notes" minoccurs="1" maxoccurs="unbounded">
          <xs:complextype>
            <xs:sequence>
              <xs:element name="Note" minoccurs="1" maxoccurs="unbounded">
                <xs:complextype>
                  <xs:simplecontent>
                    <xs:extension base="xs:string">
                      <xs:attribute type="xs:integer" name="ID">
                      </xs:attribute>
                    </xs:extension>
                  </xs:simplecontent>
                </xs:complextype>
              </xs:element><!-- EOF Note -->
            </xs:sequence>
          </xs:complextype>
        </xs:element><!-- EOF Notes-->
      </xs:element></xs:sequence>
    </xs:complextype>
  </xs:element><!-- EOF MyData -->

示例数据

<!--?xml version="1.0"?-->
<mydata>
  <version>1.0</version>
  <notes>
    <note id="1">Some text</note>
    <note id="2">Another text</note>
    <note id="3">Hello kitty!</note>
    <note id="4">Good bye, my love...</note>
  </notes>

到目前为止,这个任务看起来微不足道,对吧?

成功的秘密

成功的秘密在于正确定义 MyData 类。

请看下面的图表

正如您所看到的,MyData 类有 2 个字段

  • Version - string 属性,用于存储版本信息
  • Notes - 嵌套类,Note 类的集合

还有其他一些需要记住的事情

  • 通过 XMLSerializer 序列化和反序列化对象(类)是可能的,针对公开可见且可读写的字段!
  • 您可以使用 属性 忽略某些字段或更改其目标名称
  • 所有公开可见的类型都不能有参数
  • XmlSerializer 无法序列化或反序列化:ArrayList 的数组或 List(Of T) 的数组
  • 即使集合的属性设置为 ReadOnly,也可以序列化/反序列化集合。
  • 集合必须具有 默认 访问器

类的定义

Note

<serializable()> _
Public Class Note
    Private iId As Integer = 0
    Private sText As String = String.Empty

    <XmlAttribute("ID")> _
    Public Property ID() As Integer
        Get
            Return iId
        End Get
        Set(value As Integer)
            iId = value
        End Set
    End Property

    <XmlText()> _
    Public Property Text() As String
        Get
            Return sText
        End Get
        Set(value As String)
            sText = value
        End Set
    End Property

End Class

Note 类的定义非常简单。 它的 XML 表示必须是

<note id="1">Some Text</note>

而不是

<note><id>1</id><text>Some Text</text></note>

<note id="1"><text>Some Text</text></note>

这就是为什么 ID 字段被定义为 XML 元素 (Note) 的 XmlAttribute,而 Text 被定义为 XmlText

NotesCollection

Partial Public Class MyData
    Public Class NotesCollection
        Inherits CollectionBase

        Public Sub Add(ByVal oNote As Note)
            List.Add(oNote)
        End Sub

        Public Sub Remove(ByVal index As Integer)
            If index > Count - 1 Or index < 0 Then
                Console.WriteLine("Index not valid!")
            Else
                List.RemoveAt(index)
            End If
        End Sub

        'default accessor to the Note class
        Default Public ReadOnly Property Item(ByVal index As Integer) As Note
            Get
                Return CType(List.Item(index), Note)
            End Get
        End Property

    End Class
End Class

请看 Item 属性的定义! 它是 Note 类的默认访问器!

最后是 MyData 类的定义

<serializable()> _
<XmlRoot("MyData")> _
Public Class MyData

    Private oNotes As NotesCollection = New NotesCollection
    Private sVersion As String = "1.0"

    <XmlElement("Version")> _
    Public Property Version As String
        Get
            Return sVersion
        End Get
        Set(value As String)
            sVersion = value
        End Set
    End Property

    <XmlArray("Notes"), XmlArrayItem("Note", GetType(Note))> _
    Public ReadOnly Property Notes As NotesCollection
        Get
            Return oNotes
        End Get
    End Property

End Class

对于 Notes 属性的序列化和反序列化,最重要的一行是

<XmlArray("Notes"), XmlArrayItem("Note", GetType(Note))> _

这意味着:将此属性视为对象数组(XML 元素)。

用法

Module Module1

    Sub Main()
        'consts:
        Const sFile As String = "D:\Notes.xml"
        'vars:
        Dim oData As MyData = Nothing
        Dim oNote As Note = Nothing
        Dim ans As ConsoleKeyInfo = Nothing

        'create instance of MyData 
        oData = New MyData
        'add notes
        For i As Integer = 1 To 26
            oNote = New Note
            With oNote
                .ID = i
                .Text = Chr(64 + i)
            End With
            oData.Notes.Add(oNote)
        Next
        'serialize data
        Console.WriteLine("Starting serialization...")
        SerializeMyData(oData, sFile)
        Console.WriteLine("Do you want to see XML file (y|n)?")
        ans = Console.ReadKey(True)
        If ans.KeyChar = "y" Then Process.Start(sFile)

        'deserialize data
        oData = Nothing 'clean up!
        oData = New MyData() 'create new instance of MyData class
        Console.WriteLine("Starting deserialization...")
        Console.WriteLine("")
        oData = DeserializeMyData(oData, sFile)
        Console.WriteLine("Ready!")
        'display
        Console.WriteLine("")
        Console.WriteLine("Version: {0}", oData.Version)
        Console.WriteLine("Notes:")
        For Each n As Note In oData.Notes
            Console.WriteLine("ID: {0}; Text: {1}", n.ID, n.Text)
        Next
        Console.WriteLine("")
        Console.WriteLine("Done!")
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()

    End Sub

    Private Sub SerializeMyData(ByVal obj As MyData, ByVal sFileName As String)
        Dim serializer As XmlSerializer = Nothing
        Dim fs As System.IO.FileStream = Nothing

        Try
            serializer = New XmlSerializer(obj.GetType())
            fs = New System.IO.FileStream(sFileName, IO.FileMode.Create)
            serializer.Serialize(fs, obj)
            fs.Close()

        Catch ex As Exception
            Console.WriteLine(ex.Message)

        Finally
            If Not fs Is Nothing Then fs.Close()
            fs = Nothing
            serializer = Nothing
        End Try
    End Sub

    Private Function DeserializeMyData(ByVal obj As MyData, ByVal sFileName As String) As MyData
        Dim serializer As XmlSerializer = Nothing
        Dim fs As System.IO.FileStream = Nothing

        Try
            serializer = New XmlSerializer(obj.GetType())
            fs = New System.IO.FileStream(sFileName, IO.FileMode.Open)
            obj = serializer.Deserialize(fs)
            fs.Close()

        Catch ex As Exception
            Console.WriteLine(ex.Message)

        Finally
            If Not fs Is Nothing Then fs.Close()
            fs = Nothing
            serializer = Nothing
        End Try

        Return obj

    End Function

End Module

关注点

我希望阅读此技巧可以节省您在编写代码时可能遇到的问题的时间。

如果您有任何问题,请提问。

历史

  • 2014-06-19 - 初始版本
© . All rights reserved.