序列化其他项目的派生类会抛出“该类型不是预期的类型。使用 XmlInclude 或 SoapInclude 属性...”

问题描述 投票:0回答:1

假设我在 Visual Studio 中有两个项目。基础项目和派生项目。

基础项目包含一个基类:

Public Class Car
    Property Name As String
End Class

以及用于序列化的通用类

Public Class Seriallizer(Of T)
Function SerializeObjToString(obj As T) As String
        Dim serializer As XmlSerializer
        Dim memStream As New MemoryStream
        Dim xmlWriter As New XmlTextWriter(memStream, Encoding.UTF8)
        Try
            serializer = New XmlSerializer(GetType(T))
            serializer.Serialize(xmlWriter, obj)
            Dim xml As String
            xml = Encoding.UTF8.GetString(memStream.GetBuffer)
            xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)))
            xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1))
            Return xml
        Catch e As Exception
            MsgBox($"Error writing data to string: {e.Message}")
        Finally
            xmlWriter.Close()
            memStream.Close()
            xmlWriter = Nothing
            serializer = Nothing
            memStream = Nothing
        End Try
        Return ""
    End Function

现在我在我的第二个项目中派生了类“SpecialCar”。

Public Class SpecialCar
    Inherits Car
    Sub New()
        MyBase.New
    End Sub
    Property VentorName As String
End Class

我的带有派生类的项目引用了基类项目。 当我现在想要序列化派生类时,我收到错误:

Dim spezicalCar As New SpecialCar
Dim serializer As New Seriallizer(Of Car)
Dim str As String = serializer.SerializeObjToString(spezicalCar)

错误:

The type Derived.SpecialCar was not expected. 
Use the XmlInclude or SoapInclude attribute to specify types that are not 
known statically

我知道,我可以使用 XMLInclude 属性,但这在这种情况下不起作用,因为基类项目不知道派生类项目。在其他 SO 帖子中,我读到了一些有关为类提供 xml 命名空间的内容,但我不知道该怎么做。 如何将派生类序列化为 XML?

xml vb.net serialization derived-class base-class
1个回答
0
投票

经过一天的研究,我找到了我的问题的解决方案。

首先,您需要编写自己的序列化器类(应该是通用的,您可以将它用于所有抽象(Base/MustInherit)类)。 以下 SO 和 codeproject.com 链接帮助我解决了任务:

XML 序列化和继承类型

https://www.codeproject.com/Articles/8644/XmlSerializer-and-not-expected-Inherited-Types

在这两个链接中,您将找到类似的解决方案。请注意,在代码项目链接中,您必须在评论部分中查找通用解决方案。

这是我最终的通用序列化器类,您可以将其放置在带有基类的项目中,或者放置在专用项目中,并将基类中的引用添加到专用项目中。

Imports System.Xml
Imports System.Xml.Schema
Imports System.Xml.Serialization

Public Class GenericSerializer(Of T)
    Implements IXmlSerializable

    Public Shared Widening Operator CType(p As GenericSerializer(Of T)) As T
        Return IIf(p Is Nothing, Nothing, p.Derived)
    End Operator

    Public Shared Widening Operator CType(p As T) As GenericSerializer(Of T)
        Return IIf(p Is Nothing, Nothing, New GenericSerializer(Of T)(p))
    End Operator

    Private _derived As T
    Public ReadOnly Property Derived As T
        Get
            Return _derived
        End Get
    End Property

    Public Sub New()
        'Nothing to do here
    End Sub

    Public Sub New(ByVal derived As T)
        Me._derived = derived
    End Sub

    Public Sub ReadXml(reader As XmlReader) Implements IXmlSerializable.ReadXml
        Dim typeAttrib As String = reader.GetAttribute("type")
        ' Ensure the Type was Specified
        If (typeAttrib Is Nothing) Then
            Throw New ArgumentNullException(("Unable to Read Xml Data for Abstract Type '" _
                    + (GetType(T).Name + "' because no 'type' attribute was specified in the XML.")))
        End If

        Dim type As Type = Type.GetType(typeAttrib)
        ' Check the Type is Found.
        If (type Is Nothing) Then
            Throw New InvalidCastException(("Unable to Read Xml Data for Abstract Type '" _
                    + (GetType(T).Name + "' because the type specified in the XML was not found.")))
        End If

        If Not type.IsSubclassOf(GetType(T)) Then
            Throw New InvalidCastException(("Unable to Read Xml Data for Abstract Type '" _
                    + (GetType(T).Name + ("' because the Type specified in the XML differs ('" _
                    + (type.Name + "').")))))
        End If

        reader.ReadStartElement()
        Me._derived = New XmlSerializer(type).Deserialize(reader)
        reader.ReadEndElement()
    End Sub

    Public Sub WriteXml(writer As XmlWriter) Implements IXmlSerializable.WriteXml
        Dim type As Type = Me._derived.GetType
        writer.WriteAttributeString("type", type.AssemblyQualifiedName.ToString)
        Call New XmlSerializer(_derived.GetType).Serialize(writer, _derived)
    End Sub

    Public Function GetSchema() As XmlSchema Implements IXmlSerializable.GetSchema
        Return Nothing
    End Function

End Class

不幸的是,我必须将基类包装在另一个类中才能使用自定义序列化器。

Imports System.Xml.Serialization

Public Class Car
    Property Name As String
End Class

Public Class XMLCar
    <XmlElement(GetType(GenericSerializer(Of Car)), ElementName:="Car")>
    Property Car As Car
End Class

像这样调用序列化器:

Dim spezicalCar As New SpecialCar
Dim serializer As New Seriallizer
Dim xmlCar As New XMLCar With {.Car = spezicalCar}
Dim str As String = serializer.SerializeObjToString(Of XMLCar)(xmlCar)

您还可以在派生类对象列表上使用它:

<XmlArrayItem(GetType(GenericSerializer(Of Car)))>
Property CarList As List(Of Car)
© www.soinside.com 2019 - 2024. All rights reserved.