繁体   English   中英

如何重写XMLSerializer(序列化和反序列化)

[英]How to Override XMLSerializer (Serialize and Deserialize)

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

我有一个完整的类库,其中有一堆复杂的类型,等等,它们代表了我们系统中无法更改的不同消息。 根据Message_Type(XMLRoot中的一个属性),需要不同的属性和元素。 如果我尝试反序列化具有错误信息的对象,则该对象不会引发异常,而我希望这样做。 XSD验证不起作用,因为两种不同类型的元素名称通常相同,但是每种类型需要不同的内容。 在我的类上使用XMLAttribute和XMLElement标记,没有“ Required”属性。 即使Elements上有一个“ IsNullable”属性(而不是Attributes),XMLSerializer似乎在反序列化过程中也没有对此加以注意。

因此,我决定尝试创建一个附加的“ Required”属性:

<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

因此,现在的问题是如何自定义序列化/反序列化过程以利用此新的“ Required”属性。 如果我继承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()方法,但是我想要一些更通用的方法。 您可以提出的任何帮助或指导将不胜感激!

遵循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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM