Vb.net - create xml from Data Table rows

I want to create/save some data in an xml format, which is saved in a 2 fields data table object (the second field “target” holds no data).

The final solution should look like this:

<?xml version="1.0" encoding="utf-8"?>
<body>
  <trans-unit>
    <source>Some Text-1</source>
    <target />
  </trans-unit>
  <trans-unit>
    <source>Some Text-2</source>
    <target />
  </trans-unit>
  <trans-unit>
    <source>Some Text-3</source>
    <target />
  </trans-unit>
  ...
</body>

So far I managed to produce this:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<body>
  <trans-unit>
    <source xmlns="Some Text-1" />
    <target />
    <source xmlns="Some Text-2" />
    <target />
    <source xmlns="Some Text-3" />
    <target />
    ...
</body>

with this code(too many comments, I know…):

Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
    Dim xdoc As New XmlDocument
    'Dim xTransUnit As List(Of XmlElement) = New List(Of XmlElement)

    Dim xRoot As XmlElement = xdoc.CreateElement("body")
    Dim xTU As XmlElement = xdoc.CreateElement("trans-unit")
    'Dim xSource As XmlElement = xdoc.CreateElement("source")
    'Dim xTarget As XmlElement = xdoc.CreateElement("target")
    xdoc.AppendChild(xdoc.CreateXmlDeclaration("1.0", "utf-8", "no"))
    xdoc.AppendChild(xRoot)

    For Each row As DataRow In dtTranslation.Rows
        Dim xList As List(Of XmlElement) = New List(Of XmlElement)
        'xList.Add(xdoc.CreateElement("source", row("Source").ToString()))
        'xList.Add(xdoc.CreateElement("target", row("Target").ToString()))
        'xList.Add(xdoc.CreateElement("source", row("Source").ToString()))
        'xList.Add(xdoc.CreateElement("target", row("Target").ToString()))
        'Dim xSource As XmlElement = xdoc.CreateElement("source")
        'Dim xTarget As XmlElement = xdoc.CreateElement("target")
        'xSource.InnerText = row("Source").ToString()
        'xTarget.InnerText = row("Target").ToString()
        'xTU.AppendChild(xFields(0))
        'xTU.AppendChild(xFields(1))
        'xFields.Add(xRoot)
        Dim xSource As XmlElement = xdoc.CreateElement("source", row("Source").ToString())
        Dim xTarget As XmlElement = xdoc.CreateElement("target", row("Target").ToString())
        'xSource.InnerText = row("Source").ToString()
        'xTarget.InnerText = row("Target").ToString()
        xRoot.AppendChild(xTU)
        xTU.AppendChild(xSource)
        xTU.AppendChild(xTarget)

        'xTU.AppendChild(xFields(0))
        'xTU.AppendChild(xFields(1))
    Next

    'xTU.AppendChild(xSource)
    'xTU.AppendChild(xTarget)
    'xRoot.AppendChild(xTU)


    'xdoc.DocumentElement.SetAttribute("version", "1.2")
    'xdoc.AppendChild(xdoc.CreateElement("xliff"))
    'xdoc.CreateElement("body")

    xdoc.Save("E:\Temp\TestXML-3.xml")

End Sub

Simple and Correct Answer:

You’re creating only one <trans-unit> element outside the loop, so all your <source> and <target> elements are being added to that single node — which is wrong.

Also, you’re using CreateElement(name, value) incorrectly — the second argument is not the text content, it’s treated as a namespace.


Fixed and clean version of your code:

Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
    Dim xdoc As New XmlDocument()

    ' Create XML declaration
    Dim declaration As XmlDeclaration = xdoc.CreateXmlDeclaration("1.0", "utf-8", Nothing)
    xdoc.AppendChild(declaration)

    ' Create root element <body>
    Dim xRoot As XmlElement = xdoc.CreateElement("body")
    xdoc.AppendChild(xRoot)

    ' Loop through each row to create <trans-unit>
    For Each row As DataRow In dtTranslation.Rows
        Dim xTU As XmlElement = xdoc.CreateElement("trans-unit")

        Dim xSource As XmlElement = xdoc.CreateElement("source")
        xSource.InnerText = row("Source").ToString()

        Dim xTarget As XmlElement = xdoc.CreateElement("target")
        ' Optional: leave empty if "Target" is blank
        If Not IsDBNull(row("Target")) Then
            xTarget.InnerText = row("Target").ToString()
        End If

        xTU.AppendChild(xSource)
        xTU.AppendChild(xTarget)
        xRoot.AppendChild(xTU)
    Next

    ' Save to file
    xdoc.Save("E:\Temp\TestXML-3.xml")
End Sub

Output will now be exactly like:

<?xml version="1.0" encoding="utf-8"?>
<body>
  <trans-unit>
    <source>Some Text-1</source>
    <target />
  </trans-unit>
  <trans-unit>
    <source>Some Text-2</source>
    <target />
  </trans-unit>
  ...
</body>

Let me know if you want to include namespaces or other attributes.