Remove element from XML based on condition [duplicate] - c#

XML
<WorkTable>
<Days>
<Day id="0" name="Monday"/>
<Day id="1" name="Tuesday"/>
<Day id="2" name="Wednesday"/>
<Day id="3" name="Thursday" />
<Day id="4" name="Friday"/>
<Day id="5" name="Saturday"/>
<Day id="6" name="Sunday"/>
</Days>
<SpecialDays>
<Day date="22.07.2015"/>
<Day date="24.07.2015"/>
</SpecialDays>
</WorkTable>
This code doesn't remove the node from xml. Could you help me to find problem?
XmlDocument doc = new XmlDocument();
doc.Load(localXMLpath + xmlFileName);
XmlNode delNode= doc.SelectSingleNode("/WorkTable/SpecialDays/Day[#date='24.07.2015']");
delNode.ParentNode.RemoveChild(delNode);
doc.Save(localXMLpath + xmlFileName);

This should works:
XDocument xdoc = XDocument.Load(filename);
xdoc.Element("WorkTable").Element("SpecialDays").Elements("Day")
.Where(x => (string)x.Attribute("date") == "24.07.2015")
.Remove();
xdoc.Save(filename);

Your code works OK, the problem is that you're trying to overwrite the file you've read the data from.
See this answer C# : the close method of Xml.Load(file)

Related

How to select a specific child node and add new Child node in it

I am an XML beginner and really need your help due to this issue already blocked me for hours.
I am using C# and write some code to control my XML.
Right now I have a
string StrSec = "Scetion A"
My Code is like below:
XmlDocument doc = new XmlDocument();
doc.LoadXml(myPath);
in here i want to do something like this:
Check each of sections in current XML, if section.name == StrSec
then put the new case into that specific section
if section.name != StrSec, then put case into Section B
XmlElement newCase = doc.CreateElement("case");
XmlElement newName= doc.CreateElement("name");
newName.InnerText = "Case 4";
XmlElement newDes= doc.CreateElement("description");
newDes.InnerText = "something here";
newCase .AppendChild(newName);
newCase .AppendChild(newDes);
This is current XML
<?xml version="1.0" encoding="UTF-8"?>
<suite>
<sections>
<section>
<name>Section A</name>
<cases>
<case>
<name>Case 1</name>
<description>something here</description>
</case>
<case>
<name>Case 2</name>
<description>something here</description>
</case>
</cases>
</section>
<section>
<name>Section B</name>
<cases>
<case>
<name>Case 3</name>
<description>something here</description>
</case>
</cases>
</section>
</sections>
</suite>
The final result should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<suite>
<sections>
<section>
<name>Section A</name>
<cases>
<case>
<name>Case 1</name>
<description>something here</description>
</case>
<case>
<name>Case 2</name>
<description>something here</description>
</case>
<case>
<name>Case 4</name>
<description>something here</description>
</case>
</cases>
</section>
<section>
<name>Section B</name>
<cases>
<case>
<name>Case 3</name>
<description>something here</description>
</case>
</cases>
</section>
</sections>
</suite>
Thanks.
Using XDocument instead of XmlDocument (as it's slightly easier to work with):
var doc = XDocument.Parse(text);
foreach (var section in doc.Descendents("section").Where(x => x.Element("name").Value == "Section A"))
{
section.Element("cases").Add(
new XElement("case",
new XElement("name", "Case 4"),
new XElement("description", "something here")
)
);
}
You can find further documentation on XElement here.

C# returning an XML element with ancestors and self, but no other children

I have an xml doc layout like so
<?xml version="1.0" encoding="utf-8" ?>
<Document name="document name">
<DataSet name="dataset 1">
<Dimension name="dimension 1">
<Metric name="metric 1">
<Data value="1">
<Data value="2">
<Data value="3">
</Metric>
<Metric name="metric 2">
<Data value="4">
<Data value="5">
<Data value="6">
</Metric>
</Dimension>
<Dimension name="dimension 2">
<Metric name="metric 3">
<Data value="6">
<Data value="7">
<Data value="8">
</Metric>
<Metric name="metric 4">
<Data value="9">
<Data value="10">
<Data value="11">
</Metric>
</Dimension>
</DataSet>
</Document>
I am attempting to split the Metrics out with their ancestors but not with their siblings. For example I want a file to look like...
<?xml version="1.0" encoding="utf-8" ?>
<Document name="document name">
<DataSet name="dataset 1">
<Dimension name="dimension 1">
<Metric name="metric 1">
<Data value="1">
<Data value="2">
<Data value="3">
</Metric>
</Dimension>
</DataSet>
</Document>
and file 2 to look like ...
<?xml version="1.0" encoding="utf-8" ?>
<Document name="document name">
<DataSet name="dataset 1">
<Dimension name="dimension 1">
<Metric name="metric 2">
<Data value="4">
<Data value="5">
<Data value="6">
</Metric>
</Dimension>
</DataSet>
</Document>
My attempt to solve this was to create a method that would accept an xmlfile, outputDirectory, elementName, attributeName, and attributeValue.
This would allow me to search the document for a specific metric and pull it out into its own file with its entire tree, but not with it's siblings.
at the top of my method i have...
XDocument doc = XDocument.Load(xmlFile);
IEnumerable<XElement> elementsInPath = doc.Descendants()
.Elements(elementName)
.Where(p => p.Attribute(attributeName).Value.Contains(attributeValue))
.AncestorsAndSelf()
.InDocumentOrder()
.ToList();
However when I iterate through the elementsInPath The output gives all of the parents of the matched "Metric" with each parent return all of its children, and the only child i want present is the one that was matched by the input params.
Any help would be appreciated. Just for reference I save the files using the following snippet
int i = 1;
foreach (XElement element in elementsInPath)
{
XDocument tmpDoc = new XDocument();
tmpDoc.Add(element);
tmpDoc.Save(outputDirectory + elementName + "_" + i + ".xml");
i++;
}
Also to note, If I use the following code I get the exact metrics I am looking for, but i need to encapsulate them within their parents.
IEnumerable<XElement> elementsInPath = doc.Descendants(elementName)
.Where(p => p.Attribute(attributeName).Value.Contains(attributeValue))
.InDocumentOrder()
.ToList();
So you're really building a new document for each Metric element. The most straightforward way to do this would be:
foreach (XElement el in doc.Root.Descendants("Metric"))
{
XElement newDoc =
new XElement("Document",
new XAttribute(doc.Root.Attribute("name")),
new XElement("DataSet",
new XAttribute(el.Parent.Parent.Attribute("name")),
new XElement("Dimension",
new XAttribute(el.Parent.Attribute("name")),
el)
)
)
);
newDoc.Save(el.Attribute("name").Value.Replace(" ", "_") + ".xml");
}
This hopefully illustrates how the dynamic version of this would work - iterate through the ancestors and create new elements based on them:
foreach (XElement el in doc.Root.Descendants("Metric"))
{
XElement newDoc = el;
foreach(XElement nextParent in el.Ancestors()) // iterate through ancestors
{
//rewrap in ancestor node name/attributes
newDoc = new XElement(nextParent.Name, nextParent.Attributes(), newDoc);
}
newDoc.Save(el.Attribute("name").Value.Replace(" ", "_") + ".xml");
}
Just using the Ancestors() functionality won't work, because when you tried to add them it would also add their children (the siblings of the element you're trying to split on).

How to remove XML node by attribute

XML
<WorkTable>
<Days>
<Day id="0" name="Monday"/>
<Day id="1" name="Tuesday"/>
<Day id="2" name="Wednesday"/>
<Day id="3" name="Thursday" />
<Day id="4" name="Friday"/>
<Day id="5" name="Saturday"/>
<Day id="6" name="Sunday"/>
</Days>
<SpecialDays>
<Day date="22.07.2015"/>
<Day date="24.07.2015"/>
</SpecialDays>
</WorkTable>
This code doesn't remove the node from xml. Could you help me to find problem?
XmlDocument doc = new XmlDocument();
doc.Load(localXMLpath + xmlFileName);
XmlNode delNode= doc.SelectSingleNode("/WorkTable/SpecialDays/Day[#date='24.07.2015']");
delNode.ParentNode.RemoveChild(delNode);
doc.Save(localXMLpath + xmlFileName);
This should works:
XDocument xdoc = XDocument.Load(filename);
xdoc.Element("WorkTable").Element("SpecialDays").Elements("Day")
.Where(x => (string)x.Attribute("date") == "24.07.2015")
.Remove();
xdoc.Save(filename);
Your code works OK, the problem is that you're trying to overwrite the file you've read the data from.
See this answer C# : the close method of Xml.Load(file)

Unable to select XElements with LINQ

I'm trying to select nodes using LINQ but I cannot understand why this simple query does not work.
My xml is like this:
<config>
<func_list current_app="GSCC" flag="0">
<func id="GSCC">
<BLOCKS>
<BLOCK id="1" type="ACTIONS">
<ITEMS>
<ITEM id="1" type="UINT32" size="1" value="1" />
<ITEM id="2" type="UINT32" size="1" value="5" />
<ITEM id="3" type="UINT32" size="1" value="0" />
</ITEMS>
</BLOCK>
</BLOCKS>
</func>
</func_list>
</config>
Now, I have an XElement (_funcNode) which points to 'func' node:
IEnumerable<XElement> xBlocks = from el in _funcNode.Descendants("BLOCK")
where el.Attribute("type").Value == "ACTIONS"
select el;
if (!xBlocks.Any()) return false;
Also the xBlocks.Any() throws an System.NullReferenceException exception.
Any idea?
I solved changing the query to:
IEnumerable<XElement> xBlocks = from el in _funcNode.Descendants("BLOCK")
where (string)el.Attribute("type") == "ACTIONS"
select el;
Regards.
You main problem is your Query does not add up to your xml.
IEnumerable<XElement> xBlocks = from el in _funcNode.Descendants("BLOCK")
What you want to use is
_funcNode.Descendants().Elements("BLOCK")
Try to do this
var doc = _funcNode.Descendants("BLOCK")
See what doc looks like.

C#/XML Populate TreeView with XML file

im trying to populate a treeview from a xml file.
Image of the output: http://i.stack.imgur.com/3HSCu.png
So as you can see the parents are loaded well, but the childs (the elements) not. All the child nodes are loaded in all parent nodes. But the xml is not like that.
This is the XML code:
<?xml version="1.0" encoding="utf-8" ?>
<toolbox>
<parent id="p1" caption="All Elements" class="parent">
<element id="1" name="Button" />
<element id="2" name="Label" />
<element id="3" name="Inputfield" />
<element id="4" name="Textarea" />
<element id="5" name="Image" />
<element id="6" name="Background" />
<element id="7" name="TreeView" />
</parent>
<parent id="p2" caption="Some Elements 1" class="parent">
<element id="1" name="Button" />
<element id="2" name="Label" />
<element id="3" name="Inputfield" />
</parent>
<parent id="p3" caption="Some Elements 2" class="parent">
<element id="4" name="Textarea" />
<element id="5" name="Image" />
<element id="6" name="Background" />
<element id="7" name="TreeView" />
</parent>
</toolbox>
This is the C# code:
public void loadElements(string XML_Elements, TreeView Elements_Tree){
XmlDocument XMLDocument = new XmlDocument();
XMLDocument.Load(XML_Elements);
Elements_Tree.Nodes.Clear();
Elements_Tree.BeginUpdate();
XmlNodeList XMLParent = XMLDocument.SelectNodes("toolbox/parent");
foreach(XmlNode xmlparent in XMLParent){
//add parents
string Parent_Caption = xmlparent.Attributes["caption"].Value;
TreeNode parents = Elements_Tree.Nodes.Add(Parent_Caption);
//add childs
XmlNodeList XMLChilds = XMLDocument.SelectNodes("toolbox/parent/element");
foreach (XmlNode xmlchild in XMLChilds)
{
string Child_Name = xmlchild.Attributes["name"].Value;
parents.Nodes.Add(Child_Name);
}
}
}
You have to search for elements only within current parent element. Try that:
XmlNodeList XMLChilds = XMLDocument.SelectNodes("toolbox/parent[#caption='" + Parent_Caption + "']/element");
Or maybe even better:
XmlNodeList XMLChilds = xmlparent.SelectNodes("element");
XMLDocument.SelectNodes("toolbox/parent/element") selects all nodes that match in the document. You need to get the children of the current XmlNode, not start at the XMLDocument.

Categories

Resources