如何覆盖XMLSerializer(序列化和反序列化)

时间:2017-03-22 15:21:50

标签: c# vb.net serialization

我使用的是.NET 4.52。我在VB.NET中编程,但如果你有一个C#的解决方案,我可以转置。

我有一个完整的类库,它有一堆复杂的类型等,代表我们系统中不同的消息,我无法改变。根据Message_Type(XMLRoot中的属性),需要不同的属性和元素。如果我尝试反序列化具有错误信息的对象,它不会抛出异常,我希望它。 XSD验证不起作用,因为两个不同类型的元素名称通常相同,但每种类型都需要不同的东西。在我的类上使用XMLAttribute和XMLElement标记,没有"必需"属性。即使有一个" IsNullable"在元素(但不是属性)上的属性,XMLSerializer似乎在反序列化期间不关注它。

所以,我决定尝试创建一个额外的"必需"属性:

<AttributeUsage(AttributeTargets.Property, Inherited:=False, AllowMultiple:=False)>
Public Class XMLPlusElementAttribute
      Inherits XmlElementAttribute

    Public Sub New()
        MyBase.ElementName = ElementName
        Me.m_Required = False
    End Sub
    Private m_Required As Boolean

    Public Overridable Property Required() As Boolean
        Get
            Return m_Required
        End Get
        Set(value As Boolean)
            m_Required = value
        End Set
    End Property
End Class

<AttributeUsage(AttributeTargets.Property, Inherited:=False, AllowMultiple:=False)>
Public Class XMLPlusAttributeAttribute
    Inherits XmlAttributeAttribute

    Public Sub New()
        MyBase.AttributeName = AttributeName
        Me.m_Required = False
    End Sub
    Private m_Required As Boolean

    Public Overridable Property Required() As Boolean
        Get
            Return m_Required
        End Get
        Set(value As Boolean)
            m_Required = value
        End Set
    End Property
End Class

我现在可以用它们装饰我的课程:

<Serializable>
<XmlRoot("INTERFACE")>
Public MustInherit Class WM_Interface

    Private m_Message_Type As String
    Private m_Event_DTTM As String
    Private m_Business_Unit As String

    <XMLPlusAttribute(AttributeName:="MESSAGE_TYPE", Required:=True)>
    Public Property Message_Type() As String
        Get
            Return m_Message_Type
        End Get
        Set(value As String)
            m_Message_Type = value
        End Set
    End Property

    <XMLPlusAttribute(AttributeName:="EVENT_DTTM", Required:=True)>
    Public Property Event_DTTM() As String
        Get
            Return m_Event_DTTM
        End Get
        Set(value As String)
            m_Event_DTTM = value
        End Set
    End Property

    <XMLPlusAttribute(AttributeName:="BUSINESS_UNIT", Required:=True)>
    Public Property Business_Unit() As String
        Get
            Return m_Business_Unit
        End Get
        Set(value As String)
            m_Business_Unit = value
        End Set
    End Property
End Class

<Serializable>
<XmlRoot("INTERFACE")>
Public Class WM_Interface_BOX
    Inherits WM_Interface

    Private m_Container As WM_Container_BOX

    <XMLPlusElement(ElementName:="CONTAINER", IsNullable:=False, Required:=True)>
    Public Property Container() As WM_Container_BOX
        Get
            Return m_Container
        End Get
        Set(value As WM_Container_BOX)
            m_Container = value
        End Set
    End Property
End Class

<Serializable>
<XmlRoot("INTERFACE")>
Public Class WM_Interface_FIB
    Inherits WM_Interface

    Private m_Fiber As WM_Fiber

    <XMLPlusElement(ElementName:="FIBER", IsNullable:=False, Required:=True)>
    Public Property Fiber() As WM_Fiber
        Get
            Return m_Fiber
        End Get
        Set(value As WM_Fiber)
            m_Fiber = value
        End Set
    End Property
End Class

所以现在的问题是如何自定义序列化/反序列化过程以利用这个新的&#34;必需&#34;属性。如果我继承XMLSerializer,我似乎可以覆盖这些方法,但我不知道该放什么:

Public Class XMLPlusSerializer
    Inherits XmlSerializer

    Protected Overrides Function Deserialize(reader As XmlSerializationReader) As Object
        Return MyBase.Deserialize(reader)
    End Function

    Protected Overrides Sub Serialize(o As Object, writer As XmlSerializationWriter)
        MyBase.Serialize(o, writer)
    End Sub
End Class

我知道我也可以实现ISerializable并为每个方法编写自定义的ReadXML()和WriteXML()方法,但我想要更通用的方法。我们将非常感谢您提出的任何帮助或指导!

1 个答案:

答案 0 :(得分:1)

根据dbc的建议,我采用了以下解决方案。任何有关如何优化它的建议都将非常感激:

Public Class XMLPlusSerializer
    Inherits XmlSerializer

    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(type As Type)
        MyBase.New(type)
    End Sub
    Public Sub New(xmlTypeMapping As XmlTypeMapping)
        MyBase.New(xmlTypeMapping)
    End Sub
    Public Sub New(type As Type, defaultNamespace As String)
        MyBase.New(type, defaultNamespace)
    End Sub
    Public Sub New(type As Type, extraTypes() As Type)
        MyBase.New(type, extraTypes)
    End Sub
    Public Sub New(type As Type, objOverrides As XmlAttributeOverrides)
        MyBase.New(type, objOverrides)
    End Sub
    Public Sub New(type As Type, root As XmlRootAttribute)
        MyBase.New(type, root)
    End Sub
    Public Sub New(type As Type, objOverrides As XmlAttributeOverrides, extraTypes() As Type, root As XmlRootAttribute, defaultNamespace As String)
        MyBase.New(type, objOverrides, extraTypes, root, defaultNamespace)
    End Sub
    Public Sub New(type As Type, objOverrides As XmlAttributeOverrides, extraTypes() As Type, root As XmlRootAttribute, defaultNamespace As String, location As String)
        MyBase.New(type, objOverrides, extraTypes, root, defaultNamespace, location)
    End Sub
    Public Shadows Function Deserialize(stream As Stream) As Object
        Dim ret = MyBase.Deserialize(stream)
        Dim result As XMLPlusValidateRequiredResult = ValidateRequired(ret)
        If result.IsValid = False Then
            Throw New Exception(result.ExceptionMessage)
        End If
        Return ret
    End Function
    Public Shadows Function Deserialize(textReader As TextReader) As Object
        Dim ret = MyBase.Deserialize(textReader)
        Dim result As XMLPlusValidateRequiredResult = ValidateRequired(ret)
        If result.IsValid = False Then
            Throw New Exception(result.ExceptionMessage)
        End If
        Return ret
    End Function
    Public Shadows Function Deserialize(reader As XmlSerializationReader) As Object
        Dim ret = MyBase.Deserialize(reader)
        Dim result As XMLPlusValidateRequiredResult = ValidateRequired(ret)
        If result.IsValid = False Then
            Throw New Exception(result.ExceptionMessage)
        End If
        Return ret
    End Function
    Public Shadows Function Deserialize(xmlReader As XmlReader, encodingStyle As String) As Object
        Dim ret = MyBase.Deserialize(xmlReader, encodingStyle)
        Dim result As XMLPlusValidateRequiredResult = ValidateRequired(ret)
        If result.IsValid = False Then
            Throw New Exception(result.ExceptionMessage)
        End If
        Return ret
    End Function
    Public Shadows Function Deserialize(xmlReader As XmlReader, events As XmlDeserializationEvents) As Object
        Dim ret = MyBase.Deserialize(xmlReader, events)
        Dim result As XMLPlusValidateRequiredResult = ValidateRequired(ret)
        If result.IsValid = False Then
            Throw New Exception(result.ExceptionMessage)
        End If
        Return ret
    End Function
    Public Shadows Function Deserialize(xmlReader As XmlReader, encodingStyle As String, events As XmlDeserializationEvents) As Object
        Dim ret = MyBase.Deserialize(xmlReader, encodingStyle, events)
        Dim result As XMLPlusValidateRequiredResult = ValidateRequired(ret)
        If result.IsValid = False Then
            Throw New Exception(result.ExceptionMessage)
        End If
        Return ret
    End Function

    Private Function ValidateRequired(obj As Object) As XMLPlusValidateRequiredResult
        Dim ret As New XMLPlusValidateRequiredResult()

        Try
            Dim arrPI() As PropertyInfo = obj.GetType().GetProperties(BindingFlags.Public Or BindingFlags.Instance)
            For Each pi As PropertyInfo In arrPI
                Dim xmlAttributeRequired As Attribute = pi.GetCustomAttribute(GetType(XMLPlusAttributeAttribute))
                If xmlAttributeRequired IsNot Nothing Then
                    ' If its an attribute and is required, make sure there is a value
                    Dim xmlAttributeRequiredInst As XMLPlusAttributeAttribute = DirectCast(xmlAttributeRequired, XMLPlusAttributeAttribute)
                    If xmlAttributeRequiredInst.Required = True Then
                        If IsNothing(pi.GetValue(obj)) = True Then
                            Throw New Exception(String.Format("XML Deserialization Exception, Message = Property '{0}' can't be null or empty. Attribute '{1}' must be defined.", pi.Name, xmlAttributeRequiredInst.AttributeName))
                        Else
                            If pi.PropertyType = GetType(String) Then
                                If DirectCast(pi.GetValue(obj), String) = String.Empty Then
                                    Throw New Exception(String.Format("XML Deserialization Exception, Message = Property '{0}' can't be null or empty. Attribute '{1}' must be defined.", pi.Name, xmlAttributeRequiredInst.AttributeName))
                                End If
                            End If
                        End If
                    End If
                Else
                    ' If its an element and is required, make sure there is a value
                    Dim xmlElementRequired As Attribute = pi.GetCustomAttribute(GetType(XMLPlusElementAttribute))
                    If xmlElementRequired IsNot Nothing Then
                        Dim xmlElementRequiredInst As XMLPlusElementAttribute = DirectCast(xmlElementRequired, XMLPlusElementAttribute)
                        If xmlElementRequiredInst.Required = True Then
                            Dim objElem As Object = pi.GetValue(obj)
                            If IsNothing(objElem) Then
                                'If its null, immediately throw an exception
                                Throw New Exception(String.Format("XML Deserialization Exception, Message = Element '{0}' can't be null or empty. Must contain 1 or more instances of &lt;{1}&gt;", pi.Name, xmlElementRequiredInst.ElementName))
                            Else
                                Dim objType As Type = objElem.GetType()
                                If objType.IsGenericType And (objType.GetGenericTypeDefinition() = GetType(List(Of ))) Then
                                    'If its a list, make sure Count > 0
                                    Dim objList As IList = DirectCast(objElem, IList)
                                    If objList.Count = 0 Then
                                        Throw New Exception(String.Format("XML Deserialization Exception, Message = Element '{0}' can't be null or empty. Must contain 1 or more instances of &lt;{1}&gt;", pi.Name, xmlElementRequiredInst.ElementName))
                                    Else
                                        'Iterate through each list item and validate the object of each
                                        For i As Int32 = 0 To objList.Count - 1
                                            Dim objItem As Object = objList(i)
                                            Dim result As XMLPlusValidateRequiredResult = ValidateRequired(objItem)
                                            If result.IsValid = False Then
                                                Throw New Exception(result.ExceptionMessage)
                                            End If
                                        Next
                                    End If
                                Else
                                    'If its a standard singleton object, validate the object
                                    Dim result As XMLPlusValidateRequiredResult = ValidateRequired(objElem)
                                    If result.IsValid = False Then
                                        Throw New Exception(result.ExceptionMessage)
                                    End If
                                End If
                            End If
                        End If
                    End If
                End If
            Next
            ret.IsValid = True
            ret.ExceptionMessage = String.Empty
        Catch ex As Exception
            ret.IsValid = False
            ret.ExceptionMessage = ex.ToString()
        End Try
        Return ret
    End Function

    Private Class XMLPlusValidateRequiredResult
        Private m_IsValid As Boolean
        Private m_ExceptionMessage As String

        Public Property IsValid() As Boolean
            Get
                Return m_IsValid
            End Get
            Set(value As Boolean)
                m_IsValid = value
            End Set
        End Property

        Public Property ExceptionMessage() As String
            Get
                Return m_ExceptionMessage
            End Get
            Set(value As String)
                m_ExceptionMessage = value
            End Set
        End Property
    End Class

End Class

<AttributeUsage(AttributeTargets.Property, Inherited:=False, AllowMultiple:=False)>
Public Class XMLPlusElementAttribute
    Inherits XmlElementAttribute

    Public Sub New()
        MyBase.ElementName = ElementName
        Me.m_Required = False
    End Sub
    Private m_Required As Boolean

    Public Overridable Property Required() As Boolean
        Get
            Return m_Required
        End Get
        Set(value As Boolean)
            m_Required = value
        End Set
    End Property
End Class

<AttributeUsage(AttributeTargets.Property, Inherited:=False, AllowMultiple:=False)>
Public Class XMLPlusAttributeAttribute
    Inherits XmlAttributeAttribute

    Public Sub New()
        MyBase.AttributeName = AttributeName
        Me.m_Required = False
    End Sub
    Private m_Required As Boolean

    Public Overridable Property Required() As Boolean
        Get
            Return m_Required
        End Get
        Set(value As Boolean)
            m_Required = value
        End Set
    End Property
End Class