У меня есть схема XSD и файл XML, который используется для создания компонентов во время выполнения. Эти объекты могут иметь пользовательские настройки. В файле XSD это определяется как:

<xs:complexType name="ComponentSettings">
  <xs:sequence>
    <xs:any minOccurs="0"/>
  </xs:sequence>
</xs:complexType>

И создается в XSD как:

<xs:complexType name="Component">
  <xs:sequence>
    <xs:element name="Version" type="VersionRecord"/>
    <xs:element name="Settings" type="ComponentSettings"/>
  </xs:sequence>
  <xs:attribute name="Name" type="xs:string" use="required"/>
</xs:complexType>

Я столкнулся с интересной проблемой при десериализации XML-файла. Если у меня есть компонент без настроек, а XML выглядит так:

<Settings></Settings>

У меня нет проблем.

Если же XML выглядит так:

<Settings/>

Затем десериализация завершится ошибкой после этого тега, и я получу неполную запись.

Я использую следующий код, созданный xsd2code:

<System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18060"), _
 System.SerializableAttribute(), _
 System.ComponentModel.DesignerCategoryAttribute("code"), _
 System.Xml.Serialization.XmlRootAttribute([Namespace]:="", IsNullable:=True)> _
Partial Public Class Component
    Implements System.ComponentModel.INotifyPropertyChanged

    Private versionField As VersionRecord

    Private settingsField As System.Xml.XmlElement

    Private nameField As String

    Private Shared sSerializer As System.Xml.Serialization.XmlSerializer

    '''<summary>
    '''Component class constructor
    '''</summary>
    Public Sub New()
        MyBase.New()
        Me.versionField = New VersionRecord()
    End Sub

    <System.Xml.Serialization.XmlElementAttribute(Order:=0)> _
    Public Property Version() As VersionRecord
        Get
            Return Me.versionField
        End Get
        Set(value As VersionRecord)
            If (Not (Me.versionField) Is Nothing) Then
                If (versionField.Equals(value) <> True) Then
                    Me.versionField = value
                    Me.OnPropertyChanged("Version")
                End If
            Else
                Me.versionField = value
                Me.OnPropertyChanged("Version")
            End If
        End Set
    End Property

    <System.Xml.Serialization.XmlElementAttribute(Order:=2)> _
    Public Property Settings() As System.Xml.XmlElement
        Get
            Return Me.settingsField
        End Get
        Set(value As System.Xml.XmlElement)
            If (Not (Me.settingsField) Is Nothing) Then
                If (settingsField.Equals(value) <> True) Then
                    Me.settingsField = value
                    Me.OnPropertyChanged("Settings")
                End If
            Else
                Me.settingsField = value
                Me.OnPropertyChanged("Settings")
            End If
        End Set
    End Property

    <System.Xml.Serialization.XmlAttributeAttribute()> _
    Public Property Name() As String
        Get
            Return Me.nameField
        End Get
        Set(value As String)
            If (Not (Me.nameField) Is Nothing) Then
                If (nameField.Equals(value) <> True) Then
                    Me.nameField = value
                    Me.OnPropertyChanged("Name")
                End If
            Else
                Me.nameField = value
                Me.OnPropertyChanged("Name")
            End If
        End Set
    End Property

#Region "Serialize/Deserialize"
    '''<summary>
    '''Serializes current Component object into an XML document
    '''</summary>
    '''<returns>string XML value</returns>
    Public Overridable Overloads Function Serialize(ByVal encoding As System.Text.Encoding) As String
        Dim streamReader As System.IO.StreamReader = Nothing
        Dim memoryStream As System.IO.MemoryStream = Nothing
        Try
            memoryStream = New System.IO.MemoryStream()
            Dim xmlWriterSettings As System.Xml.XmlWriterSettings = New System.Xml.XmlWriterSettings()
            xmlWriterSettings.Encoding = encoding
            Dim xmlWriter As System.Xml.XmlWriter = xmlWriter.Create(memoryStream, xmlWriterSettings)
            Serializer.Serialize(xmlWriter, Me)
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin)
            streamReader = New System.IO.StreamReader(memoryStream)
            Return streamReader.ReadToEnd
        Finally
            If (Not (streamReader) Is Nothing) Then
                streamReader.Dispose()
            End If
            If (Not (memoryStream) Is Nothing) Then
                memoryStream.Dispose()
            End If
        End Try
    End Function

    Public Overridable Overloads Function Serialize() As String
        Return Serialize(Encoding.UTF8)
    End Function

    '''<summary>
    '''Deserializes workflow markup into an Component object
    '''</summary>
    '''<param name="xml">string workflow markup to deserialize</param>
    '''<param name="obj">Output Component object</param>
    '''<param name="exception">output Exception value if deserialize failed</param>
    '''<returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
    Public Overloads Shared Function Deserialize(ByVal xml As String, ByRef obj As Component, ByRef exception As System.Exception) As Boolean
        exception = Nothing
        obj = CType(Nothing, Component)
        Try
            obj = Deserialize(xml)
            Return True
        Catch ex As System.Exception
            exception = ex
            Return False
        End Try
    End Function

    Public Overloads Shared Function Deserialize(ByVal xml As String, ByRef obj As Component) As Boolean
        Dim exception As System.Exception = Nothing
        Return Deserialize(xml, obj, exception)
    End Function

    Public Overloads Shared Function Deserialize(ByVal xml As String) As Component
        Dim stringReader As System.IO.StringReader = Nothing
        Try
            stringReader = New System.IO.StringReader(xml)
            Return CType(Serializer.Deserialize(System.Xml.XmlReader.Create(stringReader)), Component)
        Finally
            If (Not (stringReader) Is Nothing) Then
                stringReader.Dispose()
            End If
        End Try
    End Function

    '''<summary>
    '''Serializes current Component object into file
    '''</summary>
    '''<param name="fileName">full path of outupt xml file</param>
    '''<param name="exception">output Exception value if failed</param>
    '''<returns>true if can serialize and save into file; otherwise, false</returns>
    Public Overridable Overloads Function SaveToFile(ByVal fileName As String, ByVal encoding As System.Text.Encoding, ByRef exception As System.Exception) As Boolean
        exception = Nothing
        Try
            SaveToFile(fileName, encoding)
            Return True
        Catch e As System.Exception
            exception = e
            Return False
        End Try
    End Function

    Public Overridable Overloads Function SaveToFile(ByVal fileName As String, ByRef exception As System.Exception) As Boolean
        Return SaveToFile(fileName, Encoding.UTF8, exception)
    End Function

    Public Overridable Overloads Sub SaveToFile(ByVal fileName As String)
        SaveToFile(fileName, Encoding.UTF8)
    End Sub

    Public Overridable Overloads Sub SaveToFile(ByVal fileName As String, ByVal encoding As System.Text.Encoding)
        Dim streamWriter As System.IO.StreamWriter = Nothing
        Try
            Dim xmlString As String = Serialize(encoding)
            streamWriter = New System.IO.StreamWriter(fileName, False, encoding.UTF8)
            streamWriter.WriteLine(xmlString)
            streamWriter.Close()
        Finally
            If (Not (streamWriter) Is Nothing) Then
                streamWriter.Dispose()
            End If
        End Try
    End Sub

    '''<summary>
    '''Deserializes xml markup from file into an Component object
    '''</summary>
    '''<param name="fileName">string xml file to load and deserialize</param>
    '''<param name="obj">Output Component object</param>
    '''<param name="exception">output Exception value if deserialize failed</param>
    '''<returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
    Public Overloads Shared Function LoadFromFile(ByVal fileName As String, ByVal encoding As System.Text.Encoding, ByRef obj As Component, ByRef exception As System.Exception) As Boolean
        exception = Nothing
        obj = CType(Nothing, Component)
        Try
            obj = LoadFromFile(fileName, encoding)
            Return True
        Catch ex As System.Exception
            exception = ex
            Return False
        End Try
    End Function

    Public Overloads Shared Function LoadFromFile(ByVal fileName As String, ByRef obj As Component, ByRef exception As System.Exception) As Boolean
        Return LoadFromFile(fileName, Encoding.UTF8, obj, exception)
    End Function

    Public Overloads Shared Function LoadFromFile(ByVal fileName As String, ByRef obj As Component) As Boolean
        Dim exception As System.Exception = Nothing
        Return LoadFromFile(fileName, obj, exception)
    End Function

    Public Overloads Shared Function LoadFromFile(ByVal fileName As String) As Component
        Return LoadFromFile(fileName, Encoding.UTF8)
    End Function

    Public Overloads Shared Function LoadFromFile(ByVal fileName As String, ByVal encoding As System.Text.Encoding) As Component
        Dim file As System.IO.FileStream = Nothing
        Dim sr As System.IO.StreamReader = Nothing
        Try
            file = New System.IO.FileStream(fileName, FileMode.Open, FileAccess.Read)
            sr = New System.IO.StreamReader(file, encoding)
            Dim xmlString As String = sr.ReadToEnd
            sr.Close()
            file.Close()
            Return Deserialize(xmlString)
        Finally
            If (Not (file) Is Nothing) Then
                file.Dispose()
            End If
            If (Not (sr) Is Nothing) Then
                sr.Dispose()
            End If
        End Try
    End Function
#End Region

#Region "Clone method"
    '''<summary>
    '''Create a clone of this Component object
    '''</summary>
    Public Overridable Function Clone() As Component
        Return CType(Me.MemberwiseClone, Component)
    End Function
#End Region
End Class

Мне любопытно, почему это происходит, а также есть ли способ этого не испытывать.

3
VoteCoffee 19 Дек 2013 в 23:38

2 ответа

Лучший ответ

Согласно http: // msdn.microsoft.com/en-us/library/system.xml.xmlreader.isemptyelement%28v=vs.110%29.aspx, «Соответствующий узел EndElement не создается для пустых элементов».

Похоже, Microsoft целенаправленно рассматривает эти два случая по-разному, грубо пренебрегая стандартами XML.

Я создал следующий метод для исправления текста XML:

        Public Function XMLReaderPatch(rawXML As String) As String
            If String.IsNullOrEmpty(rawXML) Then Return rawXML

            'Pattern for finding items similar to <name*/> where * may represent whitespace followed by text and/or whitespace
            Dim pattern As String = "<(\S+)(\s[^<|>]*)?/>"
            'Pattern for replacing with items similar to <name*></name> where * may represent whitespace followed by text and/or whitespace
            Dim replacement As String = "<$1$2></$1>"
            Dim rgx As New Text.RegularExpressions.Regex(pattern)

            Return rgx.Replace(rawXML, replacement)
        End Function
0
VoteCoffee 8 Янв 2014 в 13:10

и семантически идентичны в XML. Если ваш код по-разному реагирует на одно, чем на другое, ваш код неисправен.

0
keshlam 24 Дек 2013 в 02:54