I have to read data from an XML file and the order is important
I have the following (simplified) XML data which I need to read:
<xmltest>
<type1>
<tekst>ABC</tekst>
</type1>
<type2>
<tekst>DEF</tekst>
</type2>
<type1>
<tekst>GHI</tekst>
</type1>
<type2>
<tekst>JKL</tekst>
</type2>
<type3>
<tekst>MNO</tekst>
</type3>
</xmltest>
The following classes are made:
public class xmltest
public property type1 as list(of type1)
public property type2 as list(of type2)
public property type3 as list(of type3)
end class
public class type1
public property tekst as string
end class
public class type2
public property tekst as string
end class
public class type3
public property tekst as string
end class
I use the following code to read the XML:
Public Sub Indlaes
Dim reader As New System.Xml.XmlTextReader("filename.txt")
Dim subreader As System.Xml.XmlReader
Dim xmlSer As System.Xml.Serialization.XmlSerializer
Dim result = New cmltest
Do While (reader.Read())
Select Case reader.NodeType
Case System.Xml.XmlNodeType.Element
subreader = reader.ReadSubtree()
xmlSer = New System.Xml.Serialization.XmlSerializer(result.GetType)
result = xmlSer.Deserialize(subreader)
subreader.Close()
End Select
Loop
reader.Close()
End Sub
In the above example I end up with 3 lists inside the xmltest but can't recreate the order
I'm thinking about using a dictionary with an ID used across the 3 lists/dictionaries, but how do I get the ID's set? Or is there any other solution?
I would really want to keep using the Deserialize function as it is used for the rest of the xml data also
The schema for your sequence of <typeN>
elements looks to be a sequence of choice elements for each of possible <typeN>
element name. As explained in Choice Element Binding Support, one way to implement this is via a list of polymorphic types, provided you know all all possible <typeN>
element names at compile time, and apply the attributes <XmlElementAttribute(String, Type)>
to the list for each possible type.
The following classes implement this approach:
' The root object with the polymorphic list corresponding to the possible types
<XmlRoot("xmltest")>
Public Class XmlTest
<XmlElement("type1", GetType(Type1)), XmlElement("type2", GetType(Type2)), XmlElement("type3", GetType(Type3))>
Public Property TypeList As List(Of TypeBase) = New List(Of TypeBase) ()
End Class
' The <TypeN> polymorphic type hierarchy
Public Class TypeBase
<XmlElement("tekst")>
Public Property Tekst As String
End Class
<XmlType("type1")>
Public Class Type1
Inherits TypeBase
End Class
<XmlType("type2")>
Public Class Type2
Inherits TypeBase
End Class
<XmlType("type3")>
Public Class Type3
Inherits TypeBase
End Class
With these classes, you can deserialize automatically using XmlSerializer
as follows, without any need for manual reading with XmlReader
, using the following generic function:
Public Function DeserializeFromFile(Of T)(filename As String) As T
Using stream = File.OpenRead(filename)
Return DirectCast(New XmlSerializer(GetType(T)).Deserialize(stream), T)
End Using
End Function
Putting it all together, to deserialize your XML file, loop through the <typeN>
items in their original order, and determine the type of each, you could do the following:
Dim test = DeserializeFromFile(Of XmlTest)(filename)
For Each item in test.TypeList
Select Case item.GetType()
Case GetType(Type1)
Console.WriteLine("Type is type 1, value is {0}.", item.Tekst)
Case GetType(Type2)
Console.WriteLine("Type is type 3, value is {0}.", item.Tekst)
Case GetType(Type3)
Console.WriteLine("Type is type 3, value is {0}.", item.Tekst)
Case Else
Throw New Exception(String.Format("Unknown type {0}", item.GetType()))
End Select
Next
Notes:
I renamed your classes and properties to follow MSFT's recommended naming conventions for Visual Basic, and applied XML serializer attributes to specify the names used in the XML file.
For similar questions in C# see Keep sort when deserialize and serialize XML using XmlSerializer and Xml Deserialization - Merging two elements into a single List<T> object.
Demo fiddle here.