【问题标题】:IXmlSerializable Dictionary problemIXmlSerializable 字典问题
【发布时间】:2011-02-09 12:55:17
【问题描述】:

我试图创建一个实现IXmlSerializable 的通用Dictionary(归功于Charles Feduke)。

这是我的试验:

Sub Main()
    Dim z As New SerializableDictionary(Of String, String)
    z.Add("asdf", "asd")
    Console.WriteLine(z.Serialize)
End Sub

结果:

<?xml version="1.0" encoding="utf-16"?><Entry key="asdf" value="asd" />

我在 WriteXml 方法的顶部放置了一个断点,我看到当它停止时,编写器根本不包含任何数据,恕我直言,它应该包含根元素和 xml 声明。


<Serializable()> _
Public Class SerializableDictionary(Of TKey, TValue) : Inherits Dictionary(Of TKey, TValue) : Implements IXmlSerializable
    Private Const EntryString As String = "Entry"
    Private Const KeyString As String = "key"
    Private Const ValueString As String = "value"
    Private Shared ReadOnly AttributableTypes As Type() = New Type() {GetType(Boolean), GetType(Byte), GetType(Char), GetType(DateTime), GetType(Decimal), GetType(Double), GetType([Enum]), GetType(Guid), GetType(Int16), GetType(Int32), GetType(Int64), GetType(SByte), GetType(Single), GetType(String), GetType(TimeSpan), GetType(UInt16), GetType(UInt32), GetType(UInt64)}
    Private Shared ReadOnly GetIsAttributable As Predicate(Of Type) = Function(t) AttributableTypes.Contains(t)
    Private Shared ReadOnly IsKeyAttributable As Boolean = GetIsAttributable(GetType(TKey))
    Private Shared ReadOnly IsValueAttributable As Boolean = GetIsAttributable(GetType(TValue))
    Private Shared ReadOnly GetElementName As Func(Of Boolean, String) = Function(isKey) If(isKey, KeyString, ValueString)

    Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
        Return Nothing
    End Function

    Public Sub WriteXml(ByVal writer As XmlWriter) Implements IXmlSerializable.WriteXml
        For Each entry In Me
            writer.WriteStartElement(EntryString)

            WriteData(IsKeyAttributable, writer, True, entry.Key)
            WriteData(IsValueAttributable, writer, False, entry.Value)

            writer.WriteEndElement()
        Next
    End Sub

    Private Sub WriteData(Of T)(ByVal attributable As Boolean, ByVal writer As XmlWriter, ByVal isKey As Boolean, ByVal value As T)
        Dim name = GetElementName(isKey)

        If attributable Then
            writer.WriteAttributeString(name, value.ToString)
        Else
            Dim serializer As New XmlSerializer(GetType(T))
            writer.WriteStartElement(name)
            serializer.Serialize(writer, value)
            writer.WriteEndElement()
        End If
    End Sub

    Public Sub ReadXml(ByVal reader As XmlReader) Implements IXmlSerializable.ReadXml
        Dim empty = reader.IsEmptyElement

        reader.Read()
        If empty Then Exit Sub

        Clear()

        While reader.NodeType <> XmlNodeType.EndElement
            While reader.NodeType = XmlNodeType.Whitespace
                reader.Read()

                Dim key = ReadData(Of TKey)(IsKeyAttributable, reader, True)
                Dim value = ReadData(Of TValue)(IsValueAttributable, reader, False)

                Add(key, value)

                If Not IsKeyAttributable AndAlso Not IsValueAttributable Then reader.ReadEndElement() Else reader.Read()

                While reader.NodeType = XmlNodeType.Whitespace
                    reader.Read()
                End While
            End While

            reader.ReadEndElement()
        End While
    End Sub

    Private Function ReadData(Of T)(ByVal attributable As Boolean, ByVal reader As XmlReader, ByVal isKey As Boolean) As T
        Dim name = GetElementName(isKey)
        Dim type = GetType(T)

        If attributable Then
            Return Convert.ChangeType(reader.GetAttribute(name), type)
        Else
            Dim serializer As New XmlSerializer(type)

            While reader.Name <> name
                reader.Read()
            End While

            reader.ReadStartElement(name)
            Dim value = serializer.Deserialize(reader)
            reader.ReadEndElement()

            Return value
        End If
    End Function

    Public Shared Function Serialize(ByVal dictionary As SerializableDictionary(Of TKey, TValue)) As String
        Dim sb As New StringBuilder(1024)
        Dim sw As New StringWriter(sb)
        Dim xs As New XmlSerializer(GetType(SerializableDictionary(Of TKey, TValue)))

        xs.Serialize(sw, dictionary)
        sw.Dispose()
        Return sb.ToString
    End Function

    Public Shared Function Deserialize(ByVal xml As String) As SerializableDictionary(Of TKey, TValue)
        Dim xs As New XmlSerializer(GetType(SerializableDictionary(Of TKey, TValue)))
        Dim xr As New XmlTextReader(xml, XmlNodeType.Document, Nothing)
        xr.Close()
              Return xs.Deserialize(xr)
    End Function

    Public Function Serialize() As String
        Dim sb As New StringBuilder
        Dim xw = XmlWriter.Create(sb)
        WriteXml(xw)
        xw.Close()
        Return sb.ToString
    End Function

    Public Sub Parse(ByVal xml As String)
        Dim xr As New XmlTextReader(xml, XmlNodeType.Document, Nothing)
        ReadXml(xr)
        xr.Close()
    End Sub

End Class

【问题讨论】:

    标签: vb.net dictionary ixmlserializable idictionary


    【解决方案1】:

    为什么它会包含一个根?您没有在Serialize 中添加一个,而是在其中创建XmlWriter...我想知道您是否应该有更像(C#,抱歉)的东西:

    public string Serialize() {
        StringBuilder sb = new StringBuilder();
        XmlSerializer ser = new XmlSerializer(
            typeof(SerializableDictionary<TKey, TValue>));
        using (XmlWriter writer = XmlWriter.Create(sb)) {
            ser.Serialize(writer, this);
        }
        return sb.ToString();
    }
    

    这使用常规的XmlSerializer 核心来处理诸如编写外部元素之类的事情。或者,更改 Serialize 以包含您选择的外部元素。

    【讨论】:

    • 我还需要 Parse 方法的帮助,因为 Deserialize 返回一个新对象,我想使用它自己的 ReadXml 来“重新创建”当前实例。
    • @Shimmy - 目前哪里出了问题?那里有什么问题/问题?
    • 不,第一个问题已经用你的 sn-p 解决了,另一个问题是,我想要一个 Parse 函数来清除当前集合并从序列化实例的字符串表示中加载项目。
    • 另外,关于这个问题:stackoverflow.com/questions/2663836/…,我能够设置类,但是当我尝试设置默认值时,它说:无法从它创建属性“设置”默认值。错误消息:XML 文档中存在错误 (1, 41)。
    • 为了保持一致性,您应该将 Parse 设为静态并返回 Parse 是其方法的类的新实例。 (你在 .NET Fx 中经常看到这一点。)如果你想要一个类似 Parse 的实例方法,你应该使用一个接受字符串参数的构造函数。
    【解决方案2】:

    不是答案,但我在 Shimmy 的代码中发现了 2 个错误(顺便说一句,谢谢),对于那些试图在 .Net 2.0 上使用它的人来说有一个问题

    这些错误是相互关联的。呼吁:

    WriteData(IsKeyAttributable, writer, True, entry.Key)
    WriteData(IsValueAttributable, writer, False, entry.Value)
    

    应该是

    WriteData(IsKeyAttributable, writer, True, DirectCast(entry.Key, TKey))
    WriteData(IsValueAttributable  AndAlso IsKeyAttributable, writer, False, CType(entry.Value, TValue))
    

    即如果 Key 不能是 XML 属性,则值不能是 XML 属性。此外,需要将 entry.Key 和 entry.Value 转换为它的 TKey/TValue,否则 XMLSerializer 稍后会抱怨。 还有

    同样的调用

    Dim value = ReadData(Of TValue)(IsValueAttributable, reader, False)
    

    应该是

    Dim value = ReadData(Of TValue)(IsValueAttributable AndAlso IsKeyAttributable, reader, False)
    

    即如果值可归因,则 Agin 检查该键是否可归因

    对于那些以 .Net 运行时 2.0 为目标的用户,您需要将 GetIsAttributable 谓词声明为

    Private Shared ReadOnly GetIsAttributable As Predicate(Of Type) = Function(t) DirectCast(AttributableTypes, IList).Contains(t)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-15
      • 2017-11-16
      • 2019-07-14
      • 2016-05-09
      • 1970-01-01
      • 2020-11-13
      相关资源
      最近更新 更多