我使用的是.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()方法,但我想要更通用的方法。我们将非常感谢您提出的任何帮助或指导!
答案 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 <{1}>", 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 <{1}>", 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