Update (Edit) XML Data using DataSet and DataRow with LINQ in C# and Vb.Net

← Prev

I have written many articles before on how to read data from an XML file using LINQ in Asp.Net. Articles such as using LINQ Contains method to do SQL Like search and how to find the next or previous XML element using LINQ , were very popular among beginners. Now, here in this article I’ll show how to update or edit XML data using a DataSet and DataRow along with LINQ methods.

A DataSet in Asp.Net , represents an in-memory cache of data retrieved from a variety of data source, such as, SQL Server. We can also use the DataSet Class to read data from an XML file. Combining DataSet with the DataRow Class, we can easily update an XML file.

In this example, I am also using LINQ methods to retrieve each element in an XML file and display it on my web page.

What is this Example?

The XML file (see below) has many node’s. However, I wish to update the Price for each selected book. I’ll allow my users to change the price on my web page. The user will select a book from the <select> dropdown element and click the Show Details button. Once clicked, the click event at the code behind section will fetch misc details about the book from the XML file (matching the selected book), and show it.

The price will display in a textbox control, which will allow the user necessary changes and finally click another button to save the new price in file.

First, you will need some XML data. Here it is.

Library.xml

Create a file with the name library.xml and add the below data to it.

<?xml version="1.0" standalone="yes"?>
<Library>
  <List>
    <Code>001</Code>
    <BookName>Computer Architecture</BookName>
    <Category>Computers</Category>
    <Price>125.60</Price>
  </List>
  <List>
    <Code>002</Code>
    <BookName>Advanced Composite Materials</BookName>
    <Category>Science</Category>
    <Price>172.56</Price>
  </List>
  <List>
    <Code>003</Code>
    <BookName>Asp.Net 4 Blue Book</BookName>
    <Category>Programming</Category>
    <Price>56.00</Price>
  </List>
  <List>
    <Code>004</Code>
    <BookName>Stategies Unplugged</BookName>
    <Category>Science</Category>
    <Price>99.99</Price>
  </List>
  <List>
    <Code>005</Code>
    <BookName>Teaching Science</BookName>
    <Category>Science</Category>
    <Price>164.10</Price>
  </List>
  <List>
    <Code>006</Code>
    <BookName>Challenging Times</BookName>
    <Category>Business</Category>
    <Price>150.70</Price>
  </List>
  <List>
    <Code>007</Code>
    <BookName>Circuit Bending</BookName>
    <Category>Science</Category>
    <Price>112.00</Price>
  </List>
  <List>
    <Code>008</Code>
    <BookName>Popular Science</BookName>
    <Category>Science</Category>
    <Price>210.40</Price>
  </List>
  <List>
    <Code>009</Code>
    <BookName>ADOBE Premiere</BookName>
    <Category>Computers</Category>
    <Price>62.20</Price>
  </List>
</Library>
The Markup

In the markup section, I have added an HTML5 <select> element with no values. I’ll add values to it dynamically, that is, through code behind procedure when the page loads. Along with it, I have 2 buttons to show and edit values to the XML file.

<head>
    <title>Linq to XML Example - Update or Edit XML Data using LINQ</title>
    <style>
        div, select, button, span {
            font:13px Verdana;
        }
        div {
            margin:5px 0;
        }
        span {
            font-weight:bold;
        }
    </style>
</head>
<body>
    <form runat="server">
        <div style="width:350px;border:solid 1px #CCC;padding:10px;">
            <p>
                <%--ADD HTML5 SELECT ELEMENT.--%>
                <select id="selBooks" runat="server">
                    <option value="">-- Select your book --</option>
                </select>

                <%--BUTTON TO SHOW THE XML DATA.--%>
                <button id="btShow" onserverclick="show_details" runat="server">
                    Show Details
                </button>
            </p>
            <div id="container" visible="false" runat="server">
                <div>
                    <span>Code: </span> <label id="lblCode" runat="server"></label>
                </div>
                <div><label id="lblXML" runat="server"></label></div>
                <div>
                    <span>Price ($):</span>
                    <input type="text" id="tbPrice" style="width:100px;" runat="server" />

                    <%--BUTTON TO SAVE (EDIT) XML DATA.--%>
                    <asp:Button 
                        style="color:#000;width:100px;margin:0 0 20px 0;" id="btSave" 
                        OnClick="save" Text="Edit" runat="server" />
                </div>
            </div>
            <p><label id="msg" runat="server"></label></p>
        </div>
    </form>
</body>
Code Behind (C#)
using System;
using System.Collections.Generic;

using System.Linq;                  // FOR Descendants.
using System.Xml;                   // FOR XmlTextReader.
using System.Xml.Linq;              // FOR XDocument.
using System.Data;                  // FOR DataSet.

public partial class SiteMaster : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, System.EventArgs e)
    {
        if (!IsPostBack)
        {
            // LOAD AND READ XML DATA.
            XmlTextReader xlRead = new XmlTextReader(Server.MapPath("library.xml"));

            while (xlRead.Read())
            {
                xlRead.MoveToElement();

                // POPULATE SELECT ELEMENT.
                switch (xlRead.NodeType)
                {
                    case XmlNodeType.Element:
                        if (xlRead.Name.Trim() == "BookName")
                        {
                            selBooks.Items.Add(xlRead.ReadString());
                        }
                        break;
                }
            }
            xlRead.Close();
        }
    }

    protected void show_details(object sender, EventArgs e)
    {
        msg.InnerHtml = "";
        ViewDetails(selBooks.Value.Trim());
    }
    
    private void ViewDetails(string sBookName)
    {
        // LOAD XML DOCUMENT.
        var xml_Doc = XDocument.Load(Server.MapPath("library.xml"));

        IEnumerable<XElement> search_books = null;
        search_books = 
            from xFi in xml_Doc.Descendants("List")
            where xFi.Element("BookName").Value.Contains(sBookName)
            select xFi;

        // NOW DISPLAY THE RESULT.
        if ((search_books != null)) {
            lblXML.InnerHtml = "";

            foreach (XElement result in search_books) {
                lblCode.InnerText = result.Element("Code").Value;
                lblXML.InnerHtml = lblXML.InnerHtml + 
                "<b> Release Date: </b>" + result.Element("ReleaseDate").Value + "<br />" + 
                "<b> Book Name: </b>" + result.Element("BookName").Value + "<br />" + 
                "<b> Category: </b> " + result.Element("Category").Value;

                tbPrice.Value = result.Element("Price").Value;
            }

            container.Visible = true;
        } 
        else {
            lblXML.InnerHtml = "Found Nothing";
        }
    }

    // SAVE (EDIT) DATA TO XML USING THE DataSet CLASS.
    protected void save(object sender, EventArgs e)
    {
        if (Double.Parse(tbPrice.Value) != 0)
        {
            using (DataSet ds = new DataSet())
            {
                ds.Dispose();
                ds.ReadXml(Server.MapPath("library.xml"));
                DataRow dr = ds.Tables["list"].NewRow();

                // I AM USING THE XML NODE "Code" TO REPRESENT THE ROW, FOR WHICH I'LL UPDATE THE PRICE.
                ds.Tables["list"].Rows[int.Parse(lblCode.InnerText) - 1]["price"] = tbPrice.Value;
                ds.AcceptChanges();

                // WRITE THE UPDATED DATA INTO THE FILE.
                ds.WriteXml(Server.MapPath("library.xml"));
            }

            ViewDetails(selBooks.Value);
            msg.InnerHtml = "Data updated sucessfully for the book <b>" + 
                selBooks.Value + "</b>";
        }
    }
}

Don’t get overwhelmed by the size of the code here. Simply focus on the save procedure at the end of the code. The remaining code using LINQ is to extract and display data on our web page.

Using the DataSet’s ReadXML() method, we’re actually pushing the data to the DataSet object. It creates a temporary table. Next, using the DataRow class, I am creating a row each for the selected XML element and at the same time assigning a new value to the price node.

ds.Tables["list"].Rows[int.Parse(lblCode.InnerText) - 1]["price"] = tbPrice.Value;

Later, I am asking it to accept or commit the changes made.

ds.AcceptChanges();

Finally, using DataSet’s writeXml(), I am saving the changes in the file.

Code Behind (Vb)
Option Explicit On

Imports System.Xml
Imports System.Data             ' FOR DataSet.

Partial Class Site
    Inherits System.Web.UI.MasterPage

    Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            ' LOAD AND READ XML DATA.
            Dim xlRead As New XmlTextReader(Server.MapPath("library.xml"))

            While xlRead.Read()
                xlRead.MoveToElement()

                ' POPULATE SELECT ELEMENT.
                Select Case xlRead.NodeType
                    Case XmlNodeType.Element
                        If Trim(xlRead.Name) = "BookName" Then
                            selBooks.Items.Add(xlRead.ReadString)
                        End If
                End Select
            End While
            xlRead.Close()
        End If
    End Sub

    Protected Sub show_details(ByVal sender As Object, ByVal e As EventArgs)
        msg.InnerHtml = ""
        ViewDetails(Trim(selBooks.Value))
    End Sub

    Private Sub ViewDetails(ByVal sBookName As String)

        ' LOAD XML DOCUMENT.
        Dim xml_Doc = XDocument.Load(Server.MapPath("library.xml"))

        Dim search_books As IEnumerable(Of XElement)
        search_books =
            (From xFi In xml_Doc.Descendants("List") _
                Where xFi.Element("BookName").Value.Contains(sBookName)
            )

        ' NOW DISPLAY THE RESULT.
        If Not search_books Is Nothing Then
            lblXML.InnerHtml = ""

            For Each result As XElement In search_books
                lblCode.InnerText = result.Element("Code").Value
                lblXML.InnerHtml = lblXML.InnerHtml & _
                    "<b> Release Date: </b>" & result.Element("ReleaseDate").Value & "<br />" & _
                    "<b> Book Name: </b>" & result.Element("BookName").Value & "<br />" & _
                    "<b> Category: </b> " & result.Element("Category").Value

                tbPrice.Value = result.Element("Price").Value
            Next

            container.Visible = True
        Else
            lblXML.InnerHtml = "Found Nothing"
        End If
    End Sub

    Protected Sub save(ByVal sender As Object, ByVal e As EventArgs)
        If Val(tbPrice.Value) <> 0 Then

            ' SAVE (EDIT) DATA TO XML USING THE DataSet CLASS.

            Using ds As New DataSet()
                ds.Dispose()
                ds.ReadXml(Server.MapPath("library.xml"))

                Dim dr As DataRow = ds.Tables("list").NewRow()
	            
                ' I AM USING THE XML NODE "Code" TO REPRESENT THE ROW, FOR WHICH I'LL UPDATE THE PRICE.
                ds.Tables("list").Rows(Val(lblCode.InnerText) - 1)("price") = tbPrice.Value

                ds.AcceptChanges()
                ds.WriteXml(Server.MapPath("library.xml")) ' WRITE THE UPDATED DATA INTO THE FILE.
            End Using

            ViewDetails(selBooks.Value)
            msg.InnerHtml = "Data updated sucessfully for the book <b>" & selBooks.Value & "</b>"
        End If
    End Sub
End Class

← Previous