locked
Deserialize XML with multiple table to List RRS feed

  • Question

  • I have a following XML (company was added) but the original xml was only with Quote table.

    <?xml version="1.0" encoding="utf-8"?>
    <ArrayOfQuote xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    
    <Quote>
        <QuoteText>Hello World</QuoteText>
        <Author>Unknown</Author>
        <Category>Some Category</Category>
        <QuoteDate>0001-01-01T00:00:00</QuoteDate>
    </Quote>
    
    <Quote>
        <QuoteText>Foo Bar</QuoteText>
        <Author>Somebody Else</Author>
        <Category>Some Other Category</Category>
        <QuoteDate>9999-12-31T23:59:59.9999999</QuoteDate>
    </Quote>
    
    <Company>
        <Name>Name1</Name>
        <City>Unknown</City>
        <Zip>34354</Zip>
    </Company>
    
    <Company>
        <Name>Name2</Name>
        <City>Unknown</City>
        <Zip>5656</Zip>
    </Company>
    
    </ArrayOfQuote>

    And i have following codes in vb.net 

    Public _Quote As List(Of Quote)
    Public _Company As List(Of Company)
    
    Sub Deserialize(filePath As String) As List(Of Quote)    
    
        Using sr As New StreamReader(filePath)
            Dim serializer As New XmlSerializer(GetType(List(Of Quote)))
           _Quote = CType(serializer.Deserialize(sr), List(Of Quote))
        End Using
    
    End Sub
    
    <Serializable()>
    Public Class Quote
        Public Property QuoteText As String
        Public Property Author As String
        Public Property Category As String
        Public Property QuoteDate As DateTime
    End Class
    
    <Serializable()>
    Public Class Company
        Public Property Name As String
        Public Property City As String
        Public Property Zip As Integer
    End Class

    Above sub converts my XML to List(Of Quote) but i want to modify the sub so that it also generates List(Of Company) as well as other tables. I will have separate XMLs in one directory and want to generate list for each table in XML. Any help? thanks.


    Friday, July 12, 2019 8:16 AM

Answers

  • This Approach would require custom deserialization. Thus just look at the problem vice-versa: How must a data structure look like to contain quotes and companies?

    Imports System.IO
    Imports System.Xml.Serialization
    
    <Serializable()>
    Public Class Quote
        Public Property QuoteText As String
        Public Property Author As String
        Public Property Category As String
        Public Property QuoteDate As DateTime
    End Class
    
    <Serializable()>
    Public Class Company
        Public Property Name As String
        Public Property City As String
        Public Property Zip As Integer
    End Class
    
    <Serializable()>
    Public Class CompaniesAndQuotes
        Public Property Companies As List(Of Company)
        Public Property Quotes As List(Of Quote)
    End Class
    
    Module Module1
        Sub Main(args As String())
    
            Dim cq As New CompaniesAndQuotes
            Dim c As New Company
            Dim q As New Quote
    
            c.Name = "ACME"
            c.City = "Los A"
            c.Zip = "999"
            q.Author = "Me"
            q.Category = "Wisdom"
            q.QuoteDate = DateTime.Now
            q.QuoteText = "Schwifty!"
            cq.Companies = New List(Of Company)
            cq.Companies.Add(c)
            cq.Quotes = New List(Of Quote)
            cq.Quotes.Add(q)
    
            Dim serialWriter As StreamWriter
            serialWriter = New StreamWriter("C:\Temp\serialXML.xml")
            Dim xmlWriter As New XmlSerializer(cq.GetType())
            xmlWriter.Serialize(serialWriter, cq)
            serialWriter.Close()
    
            Console.WriteLine("Done.")
            Console.ReadLine()
        End Sub
    End Module

    which gives you as XML:

    <?xml version="1.0" encoding="utf-8"?>
    <CompaniesAndQuotes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Companies>
        <Company>
          <Name>ACME</Name>
          <City>Los A</City>
          <Zip>999</Zip>
        </Company>
      </Companies>
      <Quotes>
        <Quote>
          <QuoteText>Schwifty!</QuoteText>
          <Author>Me</Author>
          <Category>Wisdom</Category>
          <QuoteDate>2019-07-12T15:03:00.9841628+02:00</QuoteDate>
        </Quote>
      </Quotes>
    </CompaniesAndQuotes>

    And it is easily desieralized:

    Imports System.IO
    Imports System.Xml.Serialization
    
    <Serializable()>
    Public Class Quote
        Public Property QuoteText As String
        Public Property Author As String
        Public Property Category As String
        Public Property QuoteDate As DateTime
    End Class
    
    <Serializable()>
    Public Class Company
        Public Property Name As String
        Public Property City As String
        Public Property Zip As Integer
    End Class
    
    <Serializable()>
    Public Class CompaniesAndQuotes
        Public Property Companies As List(Of Company)
        Public Property Quotes As List(Of Quote)
    End Class
    
    Module Module1
        Sub Main(args As String())
            Dim cq As New CompaniesAndQuotes
    
            Dim file As FileStream = System.IO.File.OpenRead("C:\Temp\serialXML.xml")
            Dim xmlReader As New XmlSerializer(cq.GetType())
            cq = xmlReader.Deserialize(file)
            file.Close()
            Console.WriteLine(cq.Companies(0).Name)
    
            Console.WriteLine("Done.")
            Console.ReadLine()
        End Sub
    End Module

    • Marked as answer by Shan1986 Friday, July 12, 2019 4:10 PM
    Friday, July 12, 2019 1:10 PM

All replies

  • Hi,
    you can use XElement to deserialize the input stream and then add the nodes to the list like in this console demo:

    Imports System.IO
    Imports System.Xml.Serialization
    
    Module Module21
      Sub Main()
        Try
          Call (New Demo).Execute()
        Catch ex As Exception
          Console.WriteLine(ex.ToString)
        End Try
        Console.WriteLine("Continue enter key")
        Console.ReadKey()
      End Sub
    
      Friend Class Demo
    
        Private filePath As String = "Module21XMLFile1.xml"
    
        Friend Sub Execute()
          Dim q = Deserialize(Of Quote)(filePath)
          For Each item In q
            Console.WriteLine($"{item.Author}")
          Next
          Dim c = Deserialize(Of Company)(filePath)
          For Each item In c
            Console.WriteLine($"{item.Name}")
          Next
        End Sub
    
        Private Function Deserialize(Of T)(filePath As String) As List(Of T)
          Dim xe0 = XElement.Load(filePath)
          Dim resultList As New List(Of T)
          For Each node In xe0.Descendants(GetType(T).Name)
            Dim reader As New StringReader(node.ToString())
            Dim ser As New XmlSerializer(GetType(T))
            resultList.Add(CType(ser.Deserialize(reader), T))
          Next
          Return resultList
        End Function
    
      End Class
    
    End Module
    <Serializable()>
    Public Class Quote
      Public Property QuoteText As String
      Public Property Author As String
      Public Property Category As String
      Public Property QuoteDate As DateTime
    End Class
    
    <Serializable()>
    Public Class Company
      Public Property Name As String
      Public Property City As String
      Public Property Zip As Integer
    End Class


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Friday, July 12, 2019 10:18 AM
  • Hallo Peter, Thank you very much for your answer, sorry it is my mistake that i did not write my goal. I will have more XML files in a directory and i will loop through all XML files and for the all the nodes (e.g   Quote, Company....etc) I have to create a List (of ) things from XML. But i will have all the classes typed. Is it possible to create List when reading XML according to the node present? Thanks. 

    e.g if it finds Quote in XML file then it has to generate List (of Quote) and so on. and i will define the following

    Public _Quote As List(Of Quote)
    Public _Company As List(Of Company)

    <Serializable()> Public Class Quote Public Property QuoteText As String Public Property Author As String Public Property Category As String Public Property QuoteDate As DateTime End Class <Serializable()> Public Class Company Public Property Name As String Public Property City As String Public Property Zip As Integer End Class




    • Edited by Shan1986 Friday, July 12, 2019 10:57 AM
    Friday, July 12, 2019 10:46 AM
  • Hi,
    at this case you can find the node name and then select the Type od class like in the following demo:

    Imports System.IO
    Imports System.Xml.Serialization
    
    Public Module Module22
      Sub Main()
        Try
          Call (New Demo).Execute()
        Catch ex As Exception
          Console.WriteLine(ex.ToString)
        End Try
        Console.WriteLine("Continue enter key")
        Console.ReadKey()
      End Sub
    
      Friend Class Demo
    
        Private filePath As String = "Module21XMLFile1.xml"
    
        Friend Sub Execute()
          Dim xe0 = XElement.Load(filePath)
          Dim listOfNodeTypes As New List(Of String)
          For Each node In xe0.Elements
            Dim name = node.Name.LocalName
            If Not listOfNodeTypes.Contains(name) Then listOfNodeTypes.Add(name)
          Next
          For Each nodeType In listOfNodeTypes
            Select Case nodeType
              Case GetType(Quote).Name
                For Each item In Deserialize(Of Quote)(xe0)
                  Console.WriteLine($"Quote: {item.Author}")
                Next
              Case GetType(Company).Name
                For Each item In Deserialize(Of Company)(xe0)
                  Console.WriteLine($"Company: {item.Name}")
                Next
            End Select
          Next
        End Sub
    
        Private Function Deserialize(Of T)(xe0 As XElement) As IEnumerable(Of T)
          Dim resultList As New List(Of T)
          For Each node In xe0.Descendants(GetType(T).Name)
            Dim reader As New StringReader(node.ToString())
            Dim ser As New XmlSerializer(GetType(T))
            resultList.Add(CType(ser.Deserialize(reader), T))
          Next
          Return resultList
        End Function
    
      End Class
    
    
      <Serializable()>
      Public Class Quote
        Public Property QuoteText As String
        Public Property Author As String
        Public Property Category As String
        Public Property QuoteDate As DateTime
      End Class
    
      <Serializable()>
      Public Class Company
        Public Property Name As String
        Public Property City As String
        Public Property Zip As Integer
      End Class
    
    End Module
    


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Friday, July 12, 2019 12:45 PM
  • This Approach would require custom deserialization. Thus just look at the problem vice-versa: How must a data structure look like to contain quotes and companies?

    Imports System.IO
    Imports System.Xml.Serialization
    
    <Serializable()>
    Public Class Quote
        Public Property QuoteText As String
        Public Property Author As String
        Public Property Category As String
        Public Property QuoteDate As DateTime
    End Class
    
    <Serializable()>
    Public Class Company
        Public Property Name As String
        Public Property City As String
        Public Property Zip As Integer
    End Class
    
    <Serializable()>
    Public Class CompaniesAndQuotes
        Public Property Companies As List(Of Company)
        Public Property Quotes As List(Of Quote)
    End Class
    
    Module Module1
        Sub Main(args As String())
    
            Dim cq As New CompaniesAndQuotes
            Dim c As New Company
            Dim q As New Quote
    
            c.Name = "ACME"
            c.City = "Los A"
            c.Zip = "999"
            q.Author = "Me"
            q.Category = "Wisdom"
            q.QuoteDate = DateTime.Now
            q.QuoteText = "Schwifty!"
            cq.Companies = New List(Of Company)
            cq.Companies.Add(c)
            cq.Quotes = New List(Of Quote)
            cq.Quotes.Add(q)
    
            Dim serialWriter As StreamWriter
            serialWriter = New StreamWriter("C:\Temp\serialXML.xml")
            Dim xmlWriter As New XmlSerializer(cq.GetType())
            xmlWriter.Serialize(serialWriter, cq)
            serialWriter.Close()
    
            Console.WriteLine("Done.")
            Console.ReadLine()
        End Sub
    End Module

    which gives you as XML:

    <?xml version="1.0" encoding="utf-8"?>
    <CompaniesAndQuotes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Companies>
        <Company>
          <Name>ACME</Name>
          <City>Los A</City>
          <Zip>999</Zip>
        </Company>
      </Companies>
      <Quotes>
        <Quote>
          <QuoteText>Schwifty!</QuoteText>
          <Author>Me</Author>
          <Category>Wisdom</Category>
          <QuoteDate>2019-07-12T15:03:00.9841628+02:00</QuoteDate>
        </Quote>
      </Quotes>
    </CompaniesAndQuotes>

    And it is easily desieralized:

    Imports System.IO
    Imports System.Xml.Serialization
    
    <Serializable()>
    Public Class Quote
        Public Property QuoteText As String
        Public Property Author As String
        Public Property Category As String
        Public Property QuoteDate As DateTime
    End Class
    
    <Serializable()>
    Public Class Company
        Public Property Name As String
        Public Property City As String
        Public Property Zip As Integer
    End Class
    
    <Serializable()>
    Public Class CompaniesAndQuotes
        Public Property Companies As List(Of Company)
        Public Property Quotes As List(Of Quote)
    End Class
    
    Module Module1
        Sub Main(args As String())
            Dim cq As New CompaniesAndQuotes
    
            Dim file As FileStream = System.IO.File.OpenRead("C:\Temp\serialXML.xml")
            Dim xmlReader As New XmlSerializer(cq.GetType())
            cq = xmlReader.Deserialize(file)
            file.Close()
            Console.WriteLine(cq.Companies(0).Name)
    
            Console.WriteLine("Done.")
            Console.ReadLine()
        End Sub
    End Module

    • Marked as answer by Shan1986 Friday, July 12, 2019 4:10 PM
    Friday, July 12, 2019 1:10 PM
  • Thank you very much Stefan and Peter . it works as expected .
    • Edited by Shan1986 Friday, July 12, 2019 4:10 PM
    Friday, July 12, 2019 3:42 PM