Question

I'm trying to implement the 'IXmlSerializable' interface and struggle with the 'ReadXml' implementation. Here is my serialized xml:

<?xml version="1.0" encoding="utf-16"?>
<MyClass>
  <IntProperty>100</IntProperty>
  <BoolProperty>True</BoolProperty>
  <ArrayProperty>
    <ArrayEntry MyAttr="Bob" />
    <ArrayEntry MyAttr="Alice" />
  </ArrayProperty>
  <StringProperty>Hello World!</StringProperty>
</MyClass>

There is this "little" special requirement that the deserialization must be backwards-compatible to older serialized versions which have might have a different order of elements, are missing elements or have additional (now unused) elements. How can I do this?

Public Class MyClass
  Implements IXmlSerializable

    Public Property IntProperty As Integer
    Public Property BoolProperty As Boolean
    Public Property ArrayProperty As ArrayEntry()
    Public Property StringProperty As String

    Public Sub ReadXml(reader As System.Xml.XmlReader) Implements IXmlSerializable.ReadXml
        ' to be done ...
    End Sub

    ' ...

End Class

Public Class ArrayEntry
    Public Property MyAttr As String
End Class
Was it helpful?

Solution

Here is what works for me. It's fully flexible in regard to the order of the nodes.

Public Sub ReadXml(reader As System.Xml.XmlReader) Implements IXmlSerializable.ReadXml

    reader.MoveToContent()

    While True
        Select Case reader.Name
            Case "IntProperty"
                IntProperty = CInt(reader.ReadString())
            Case "BoolProperty"
                BoolProperty = CBool(reader.ReadString())
            Case "StringProperty"
                StringProperty = reader.ReadString()
            Case "ArrayProperty"
                Dim arrayEntries As New List(Of ArrayEntry)
                If Not reader.IsEmptyElement Then
                    While reader.Read()
                        If reader.Name <> "ArrayEntry" Then Exit While
                        Dim ar As New ArrayEntry
                        ar.MyAttr = reader.GetAttribute("MyAttr")
                        arrayEntries.Add(ar)
                    End While
                End If
                ArrayProperty = arrayEntries.ToArray()
        End Select
        If Not reader.Read() Then Exit While
    End While

End Sub

OTHER TIPS

Here is some code for parsing Geocoding data that will get you most of the way there.

In your Case 'ArrayProperty' you will need to use Reader.Read for the nested elements.

   URL = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.placefinder%20where%20text%3D%22{0}%22&diagnostics=true"
    URL = String.Format(URL, sLocation)
     Try
        reader = New XmlTextReader(URL)
        'reader.WhitespaceHandling = WhitespaceHandling.None 'Disable whitespace so that you don't have to read over whitespaces
        With Address
            While (reader.Read())
                Debug.Print(reader.Name.ToString())
                Select Case reader.Name.ToString()
                    Case "Result"
                        If reader.HasAttributes Then
                            For i = 0 To CShort(reader.AttributeCount - 1)
                                reader.MoveToAttribute(i)
                                Select Case reader.Name.ToString
                                    Case "precision"
                                        .precision = reader.GetAttribute(reader.Name.ToString)
                                    Case "warning"
                                        .warning = reader.GetAttribute(reader.Name.ToString)
                                        bOK = False
                                End Select
                                reader.MoveToElement() ' Move the reader back to the element node.
                            Next
                        End If
                    Case "ErrorMessage"
                        .warning = reader.ReadString().ToString()
                    Case "quality"
                        .precision = reader.ReadString().ToString()
                    Case "line1"
                        .Street = reader.ReadString().ToString()
                    Case "city"
                        .City = reader.ReadString().ToString()
                    Case "statecode"
                        .State = reader.ReadString().ToString()
                    Case "postal"
                        .Zip = reader.ReadString().ToString()
                    Case "country"
                        .Country = reader.ReadString().ToString()
                    Case "offsetlat"
                        .Latitude = reader.ReadString().ToString()
                    Case "offsetlon"
                        .Longitude = reader.ReadString().ToString()
                End Select
            End While
        End With
    Catch ex As Exception
        bOK = False
        Address.warning = ex.Message
    End Try

For the code above I just used the code below for the "Address" class:

Private Structure Address_struct
    Public Street As String
    Public City As String
    Public State As String
    Public Zip As String
    Public Country As String
    Public Latitude As String
    Public Longitude As String
    Public precision As String
    Public warning As String
End Structure
Dim Address As Address_struct
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top