Jobokai
Jobokai

Reputation: 313

3 Attempts to delete XML node using C#

I've tried deleting a node from my XML file 3 different ways; and each way I've come up empty. I am querying a SQL database and grabbing a filename, I want to delete the entire node were the file name in the XML document is = to the SQL database result.

I'm not sure what's wrong in my code:

Background Information

fn44 is the Filename grabbed from a SQL database (all my info is in a SQL table, I need an XML file for use with JavaScript)

XML:

<?xml version="1.0" encoding="utf-8"?>
<xml>
  <bannerMain>
    <department>main</department>
    <filename>resdrop.png</filename>
    <title>This is a Title</title>
    <text>&lt;![CDATA[caption <a href="">text</a>]]&gt;</text>
  </bannerMain>
</xml>

Attempt 1 (I know that I'm not getting to the child correctly, can't seem to figure out how to fix it):

XDocument doc = XDocument.Load(Server.MapPath("~/uploads/banners.xml"));
var q = from node in doc.Descendants("bannerMain")
        let fina = node.Descendants("filename")/*PROBLEM LINE*/
        where fina != null && fina == myReader[0]/*Gets filename from SQL database*/
select node;
q.ToList().ForEach(x => x.Remove());
doc.Save(Server.MapPath("~/uploads/banners.xml"));

Attempt 2 (should work in my mind but doesn't)

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Server.MapPath("~/uploads/banners.xml"));
XmlNode nodeToDelete = xmlDoc.SelectSingleNode("/xml/bannerMain[@filename=" 
+ fn44 + "]");
if (nodeToDelete != null)
{
   nodeToDelete.ParentNode.RemoveChild(nodeToDelete);
}
xmlDoc.Save(Server.MapPath("~/uploads/banners.xml"));

Attempt 3 (similar to attempt 2)

string nodeToDelete = fn44;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Server.MapPath("~/uploads/banners.xml"));
XmlNode node = xmlDoc.SelectSingleNode(string.Format("//*[filename=\"{0}\"]"
, nodeToDelete));
if (node != null)
   xmlDoc.SelectSingleNode("xml/bannersMain").RemoveChild(node);
xmlDoc.Save(Server.MapPath("~/uploads/banners.xml"));

I want to delete the whole node where the filename is = to the filename that is grabbed from the SQL database. Any help/resources is much appreciated.

SOLVED: There's a few different options in the below answers that work out well.

Solution 1:

var xDoc = XDocument.Load(Server.MapPath("~/uploads/banners.xml"));
string fileName = fn44; //Use whatever value you found in SQL DB...

xDoc.Descendants("filename").Where(c => c.Value == fileName).Select(x => x.Parent).Remove();
xDoc.Save(Server.MapPath("~/uploads/banners.xml"));

Solution 2:

XDocument doc = XDocument.Load(Server.MapPath("~/uploads/banners.xml"));
var q = from node in doc.Descendants("bannerMain")
        let fina = node.Element("filename")
        where fina != null && fina.Value == fn44
        select node;
q.Remove();
doc.Save(Server.MapPath("~/uploads/banners.xml"));

Upvotes: 3

Views: 901

Answers (4)

Francis Ducharme
Francis Ducharme

Reputation: 4987

That seems to work for me:

        string xmlfile = Server.MapPath("~/uploads/banners.xml");
        var xDoc = XDocument.Load(xmlfile);
        string fileName = "resdrop.png"; // Value from SQL DB

        xDoc.Descendants("filename")
            .Where(c => c.Value == fileName)
            .Select(x => x.Parent)
            .Remove();
        xDoc.Save(xmlfile);

Upvotes: 4

Chuck Savage
Chuck Savage

Reputation: 11955

For Attempt #2, remove the @ sign for the filename. The @ symbol represents an Attribute, but the filename is a child-node.

If your phrase doesn't work, I'd rephrase it a little from:

"/xml/bannerMain[filename=

to

"//bannerMain[filename=

Upvotes: 1

Lee Hiles
Lee Hiles

Reputation: 1135

It's a little verbose but here is a non-linq snippet:

void DeleteNode(string fileName)
{
    XmlDocument doc = new XmlDocument();
    doc.Load(Server.MapPath("~/uploads/banners.xml"));

    //Get all the bannerMain nodes.
    XmlNodeList nodelist = doc.SelectNodes("/xml//bannerMain");

    if (nodelist != null)
    {
        foreach (XmlNode node in nodelist)
        {
            //Look for then filename child. If it contains desired value
            //delete the entire bannerMain node. Assumes order of child nodes
            //may not be a constant.
            foreach (XmlNode child in node.ChildNodes)
            {
                if (child.Name == "filename" && child.InnerText == name)
                {
                    node.ParentNode.RemoveChild(node);
                }
            }
        }

        doc.Save(Server.MapPath("~/uploads/banners.xml"));
    }
}

Upvotes: 1

Sven Grosen
Sven Grosen

Reputation: 5636

Your problem with attempt #1 is that you are trying to compare an IEnumerable<XElement> to your reader value, this should work (assuming each bannerMain only has a single filename element):

var q = from node in doc.Descendants("bannerMain") 
        let fina = node.Element("filename")//only single filename, so get just that XElement
        where fina != null && fina.Value == reader[0]//assumes reader[0] is a string value
        select node;

To remove them just do this:

q.Remove();
doc.Save(Server.MapPath("~/uploads/banners.xml"));

I ran this through LINQPad and after doing q.Remove();, here were the contents of doc:
<xml />.

Upvotes: 2

Related Questions