3 Попытки удалить узел XML с помощью C#

Я пытался удалить узел из моего XML-файла тремя разными способами; и каждый раз я подходил пустым. Я запрашиваю базу данных SQL и получаю имя файла, я хочу удалить весь узел, если имя файла в XML-документе равно = результату базы данных SQL.

Я не уверен, что не так в моем коде:

Исходная информация

fn44 — это имя файла, полученное из базы данных SQL (вся моя информация находится в таблице SQL, мне нужен файл XML для использования с 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>

Попытка 1 (знаю, что неправильно добираюсь до ребенка, не могу понять, как это исправить):

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"));

Попытка 2 (должна работать, но не работает)

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"));

Попытка 3 (аналогична попытке 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"));

Я хочу удалить весь узел, где имя файла = имени файла, взятому из базы данных SQL. Любая помощь/ресурсы очень ценятся.

РЕШЕНО: в приведенных ниже ответах есть несколько разных вариантов, которые хорошо работают.

Решение 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"));

Решение 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"));

person Jobokai    schedule 12.12.2013    source источник
comment
Почему бы не начать с XML-шаблона без имен файлов и не добавить их все, кроме того, который вы нашли в базе данных? Имеет ли это смысл ?   -  person Francis Ducharme    schedule 12.12.2013
comment
Узлы xml создаются в процессе загрузки нового файла. В это время генерируются данные как SQL, так и XML. Этот процесс у меня запущен, когда я пытаюсь удалить запись. Таким образом, удаляя файл с сервера, из документа xml, а также из базы данных SQL. У меня есть сервер и удаление SQL; удаление xml оказывается трудным.   -  person Jobokai    schedule 12.12.2013


Ответы (4)


Кажется, это работает для меня:

        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);
person Francis Ducharme    schedule 12.12.2013
comment
Если вы посмотрите на его код, Фрэнсис, вы увидите, что он не хочет удалять все узлы с именами файлов, а хочет удалить родительский узел баннера, где у него есть дочернее имя файла, соответствующее его поиску. Я бы изменил ваш подход с помощью .Select(f => f.Parent).Remove() - person Chuck Savage; 12.12.2013
comment
Правильно, пропустил все упоминание об узле. Спасибо за упоминание. - person Francis Ducharme; 12.12.2013
comment
Вы также можете указать только узлы имени файла в вашем Descendants с Descendants("filename"), чтобы быть уверенным, что какой-либо другой узел случайно не совпадает с поиском. - person Chuck Savage; 12.12.2013
comment
Теперь вы удаляете все узлы с именем файла, не проверяя, соответствует ли оно имени файла, которое он ищет. Если вы не возражаете, я могу отредактировать ваш ответ, если вы запутались. - person Chuck Savage; 12.12.2013
comment
Я бы сделал что-то вроде xDoc.Descendants(filename=+fileName+]).Select(x => x.Parent).Remove(); - person Jobokai; 12.12.2013
comment
Сделанный. Извините, если я выгляжу таким растерянным. :П - person Francis Ducharme; 12.12.2013
comment
@Jobokai Посмотрите на мое последнее редактирование, вы увидите, где использовать значение, которое вы нашли в SQL. - person Francis Ducharme; 12.12.2013
comment
@Jobokai Нет, потому что в Descendants вы ищете имена узлов, а не их значения. - person Chuck Savage; 12.12.2013
comment
Это работает! Пошел вперед и принял этот ответ. Спасибо еще раз за помощь. - person Jobokai; 12.12.2013
comment
Код очищен, чтобы будущим зрителям не приходилось использовать полосу прокрутки, чтобы увидеть код. Хороший ответ! - person Chuck Savage; 12.12.2013
comment
Я отредактировал его, включив в него функцию Server.MapPath. Это немного лучше, чем жестко закодированные абсолютные пути. Если ваши файлы будут перемещены в другое место и путь изменится, все будет работать. - person Jobokai; 12.12.2013
comment
@ChuckSavage Да, мне гораздо больше нравятся твои правки. Очень чистый. Надеюсь, этот пост поможет кому-то еще, пытающемуся выполнять аналогичные функции; с несколькими рабочими решениями и вариантами. - person Jobokai; 12.12.2013

Ваша проблема с попыткой № 1 заключается в том, что вы пытаетесь сравнить IEnumerable<XElement> со значением вашего читателя, это должно работать (при условии, что каждый баннерMain имеет только один элемент filename):

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;

Чтобы удалить их, просто сделайте это:

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

Я прогнал это через LINQPad и после выполнения q.Remove(); вот содержимое документа:
<xml />.

person Sven Grosen    schedule 12.12.2013
comment
У меня все еще есть проблема. Он не вносит никаких изменений в файл xml. - person Jobokai; 12.12.2013
comment
Спасибо! Я думаю, что это могло быть проблемой, собираюсь попробовать еще раз! - person Jobokai; 12.12.2013
comment
хорошо, это все еще не совсем так... для doc.Descendants(bannerMain) мне нужно вместо этого перейти к xml/bannerMain. xml является всеобъемлющим держателем xml. (посмотрите пример XML выше) - person Jobokai; 12.12.2013
comment
Нет, Descendants не нуждается ни в каких путях, он просматривает XML. Я проверил это, используя фрагмент xml, который вы опубликовали, и он отлично сработал. Есть ли разница в структуре между вашим примером Xml и реальным? - person Sven Grosen; 12.12.2013
comment
хм, нет, это точно так же. - person Jobokai; 12.12.2013
comment
А reader[0] заполнен правильно? вам может потребоваться явно привести это к строке, прежде чем выполнять сравнение: fina.Value == reader[0].ToString() - person Sven Grosen; 12.12.2013
comment
Хорошо, я получил это работает! Я уже принял другой ответ, но моя проблема заключалась в том, что читатель [0] не был строкой. Я переделал и все прояснилось. Спасибо за работу со мной над этим! - person Jobokai; 12.12.2013

Это немного многословно, но вот фрагмент кода без linq:

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"));
    }
}
person Lee Hiles    schedule 12.12.2013
comment
(Просто чтобы уточнить мое понимание) строка 'fileName', которую вы установили в качестве переменной; это переменная, которую я бы правильно захватил и установил из базы данных SQL ?? - person Jobokai; 12.12.2013
comment
Ага. Вы бы передали любое имя файла, которое хотите найти и удалить. - person Lee Hiles; 12.12.2013
comment
благодарю за разъяснение - person Jobokai; 12.12.2013

Для попытки № 2 удалите знак @ для имени файла. Символ @ представляет атрибут, но имя файла является дочерним узлом.

Если ваша фраза не работает, я бы немного перефразировал ее из:

"/xml/bannerMain[имя_файла=

to

"//bannerMain[имя_файла=

person Chuck Savage    schedule 12.12.2013
comment
Я не понимал, что символ @ связан с атрибутом. Спасибо, что разъяснили это. Я попытаюсь внести изменения и посмотреть, решит ли это проблему с этой опцией. - person Jobokai; 12.12.2013