-1

This is my sample.xml file. I'm looking for instruction how to read this and put content from all nodes (subnodes) to DataSet and show it in DataGrid. I can only read single Node (without subnodes).

My code is below:

Private Sub ReadXmlButton_Click() Handles ReadXmlButton.Click

    Dim filePath As String = "C:\Path\"
    DataSet.ReadXml("C:\Path\sample.xml")

    DataGridView1.DataSource = DataSet
    DataGridView1.DataMember = "CART_ID"
End Sub

But it reads only head Node without SubNodes (1000, 10.05, YES, 8, 2). I want to display all informations (example: 1000, 10.05, A1A, Triangle, 10, 1, YES, 8, 2) from first (and all)CART_ID in DataGridView.

My result: enter image description here

Expected result: enter image description here

sample.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <XML_FILE>
        <typ>xml</typ>
        <ID>
            <NR>007</NR>
        </ID>
        <PERSONAL>
            <Surname>John</Surname>
            <Name>Smith</Name>
        </PERSONAL>
        <COUNTRY>
            <CName>UK</CName>
        </COUNTRY>
          <CITY>
            <TOWN>
                <TOWN_ID>
                    <PART_ID>
                        <CART_ID>
                            <SIMPLE_ID>1000</SIMPLE_ID>
                            <SIMPLE_AREA_ID>10.05</SIMPLE_AREA_ID>
                            <PLACE_ID>
                                <SPECIFIC_ID>
                                    <id>A1A</id>
                                    <name>Triangle</name>
                                    <area>10</area>
                                    <note>1</note>
                                </SPECIFIC_ID>
                            </PLACE_ID>
                            <Control>YES</Control>
                            <Control_area>8</Control_area>
                            <Control_rest>2</Control_rest>
                        </CART_ID>
                              <CART_ID>
                            <SIMPLE_ID>2000</SIMPLE_ID>
                            <SIMPLE_AREA_ID>20.05</SIMPLE_AREA_ID>
                            <PLACE_ID>
                                <SPECIFIC_ID>
                                    <id>B1B</id>
                                    <name>Triangle</name>
                                    <area>20</area>
                                    <note>2</note>
                                </SPECIFIC_ID>
                            </PLACE_ID>
                            <Control>YES</Control>
                            <Control_area>18</Control_area>
                            <Control_rest>2</Control_rest>
                        </CART_ID>
                    </PART_ID>
                 </TOWN_ID>
            </TOWN>
        </CITY>
</XML_FILE>

UPDATED: The last thing is how to save modified descendant (for example - manually changed SIMPPLE_ID from 1000 to 5000, etc.) to original sample.xml file ? Is it possible to do it with this solution or should I look for other way ?

Artec
  • 183
  • 4
  • 16
  • You'll have to create a "flattened" class of all the fields you want from each `CART_ID`, pick all the data from the XML into an instance of that class, add each instance to a List(Of CartClass) and set the list as the grid data source (no data member). – MrGadget Nov 12 '16 at 14:18
  • @MrGadget if You can help me more I'll be grateful, I'm a beginner with programming. – Artec Nov 12 '16 at 14:28
  • Start by learning the [XmlDocument Class](https://msdn.microsoft.com/en-us/library/system.xml.xmldocument(v=vs.110).aspx) and the [List(Of T) Class](https://msdn.microsoft.com/en-us/library/6sh2ey19(v=vs.110).aspx) – MrGadget Nov 12 '16 at 16:35

2 Answers2

1

@Artec Assuming 3 things:

  • structure of your cart node is standard
  • you know the names of the node
  • you're using Windows Form since you have the DataGridView control

... instead of using dataset, first create a class with properties that correspond to the nodes of the data you need

Public Class CartItem

    Public Property Simple_ID As String
    Public Property Simple_Area_ID As String
    Public Property Place_ID As String
    Public Property Place_Name As String
    '...

End Class

Now on your Form class, grab the data from your xml file via XDocument and create a list of CartItem.

Public Class Form1

    Private _CartItems As List(Of CartItem)

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        _CartItems = New List(Of CartItem)

        'TODO: Replace ... below with your file location.
        BuildCart("...\sample.xml")
        DataGridView1.DataSource = _CartItems
    End Sub

    Private Sub BuildCart(XmlSourcePath As String)
        Dim xDoc As XDocument = XDocument.Load(XmlSourcePath)
        For Each cartElement As XElement In xDoc.Root.Descendants("CART_ID")
            Dim item As New CartItem

            With cartElement
                item.Simple_Area_ID = .Descendants("SIMPLE_ID").Value
                item.Simple_ID = .Descendants("SIMPLE_AREA_ID").Value
                item.Place_ID = .Descendants("id").Value
                item.Place_Name = .Descendants("name").Value
                '...
            End With

            _CartItems.Add(item)
        Next
    End Sub
End Class

Note: there are several ways to read xml files like XMLTextReader; XMLDocument; XPathDocument; XMLReader; etc. It's just my personal preference to go with XDocument. You'll have to do some research if you want to check out the other options. It all depends on the complexity of your xml file, your requirements and preference.

This should get you going to the right direction.

  • Thank you very much ! That is what I was looking for. I know that it wasn't in my question but how to save manually changes to source xml file. My code `Dim filename As String = "...\sample_out.xml"` `DataSet.WriteXml(filename)` doesn't save data to read file. – Artec Nov 12 '16 at 22:11
  • @Artec Glad I could help. Regarding your follow up question, I think it's too vague and requires a couple more details: – Ricardo Virtudazo Jr Nov 13 '16 at 05:53
  • @Artec oops, pressed enter too fast. So details like: are you adding a new element of the Cart_ID? are you changing the values of one of the descendants of your existing Card_ID? Also, I think that merits another question. It's relatively out of topic from the original question. – Ricardo Virtudazo Jr Nov 13 '16 at 05:55
  • Sure, It' out of the topic. Thank you very much one more time. I'll ask a new question about changing values for existing descendants of Cart_ID (without adding new elements). – Artec Nov 13 '16 at 09:46
0

@Artec I haven't used WinForms for the longest time and I'm not sure if there's an easier way to determine which specific row and column was changed. For now, I'm just going for the simplest and basic solution to set you to the right direction.

First off, we can't just directly replace the dataset back to the xml because of your xml structure. There's no way of telling that the code needs to go few levels deep into the CART_ID element. So, we'll just be reversing the approach of loading the xml.

Using the same CartItem class from my previous answer, we cast the DataGridView dataset to the list of CartItem. Update the xDoc elements with the values of the list and save the xDocument back to the xml.

Public Class Form1

    Private _CartItems As List(Of CartItem)
    'TODO: Replace ... with the file location.
    Private _FilePath As String = "...\source.xml"

    '... Button1_Click and BuildCart in here.

    Private Sub UpdateXmlButton_Click(sender As Object, e As EventArgs) Handles UpdateXmlButton.Click
        _CartItems = DirectCast(DataGridView1.DataSource, List(Of CartItem))
        UpdateXml()
        MessageBox.Show("Update Done.")
    End Sub

    Private Sub UpdateXml()
        Dim xDoc As XDocument = XDocument.Load(_FilePath)
        Dim cartIDs As IEnumerable(Of XElement) = xDoc.Root.Descendants("CART_ID")

        For index As Integer = 0 To cartIDs.Count - 1
            Dim cartElement As XElement = cartIDs.ElementAt(index)
            Dim item As CartItem = _CartItems(index)

            With cartElement
                .Descendants("SIMPLE_ID").Value = item.Simple_Area_ID
                .Descendants("SIMPLE_AREA_ID").Value = item.Simple_ID
                .Descendants("id").Value = item.Place_ID
                .Descendants("name").Value = item.Place_Name
                '...
            End With
        Next

        xDoc.Save(_FilePath)
    End Sub
End Class

You're practically changing the elements of the loaded XDocument in memory and throwing it back into the xml file. There's definitely a lot of improvement that can be done, like:

  • looping through the properties instead of coding each property in a line. This can be achieved with the combination of reflection and dictionary.
  • updating only the elements that were changed instead of changing the entire thing
  • etc.

But code above should get you started.

  • Ricardo that's it !Thank you for improvement and for your help. Now I can try to start build next module. – Artec Nov 14 '16 at 16:00