XML element removal, just need to delete one element not the parent - c#

Basically what I am trying to do is remove a VSLOC from the list. I don't want to remove everything that belongs to it.
<?xml version="1.0"?>
<GarageNumbers>
<G554>
<id>G554</id>
<VSLOC>V002</VSLOC>
<VSLOC>V003</VSLOC>
<VSLOC>V002</VSLOC>
</G554>
<G566>
<id>G566</id>
<VSLOC>V002</VSLOC>
<VSLOC>V003</VSLOC>
<VSLOC>V002</VSLOC>
</G566>
<G572>
<id>G572</id>
<VSLOC>V001</VSLOC>
<VSLOC>V002</VSLOC>
</G572>
</GarageNumbers>
So, what I have setup is a combobox that I select a G# from which brings up all the VSLOC associated with it in a Listbox. What I need to do is to select a item from the list box and remove the line from the listbox and from the xml document using a button. I have all this setup but when I hit the button it deletes G554 and all the elements with in.
So if I want to select V002 from the list in G554 I want it to just remove that VSLOC with that innertext.
XmlDocument xDoc = new XmlDocument();
xDoc.Load(Application.StartupPath + "/xmlData.xml");
foreach (XmlNode xNode in xDoc.SelectNodes("GarageNumbers/G554"))
if (xNode.SelectSingleNode("VSLOC").InnerText == "V002")
xNode.ParentNode.RemoveChild(xNode);
xDoc.Save(Application.StartupPath + "/xmlData.xml");

You should be able to drill down to the desired elements then remove them. For example, assuming your XML is in an XElement, this approach would work:
string targetCategory = "G554";
string vsloc = "V002";
xml.Element(targetCategory)
.Elements("VSLOC")
.Where(e => e.Value == vsloc)
.Remove();
If you're using an XDocument then add the Root property: xml.Root

var xDoc = XDocument.Load(fname);
var node = xDoc.Descendants("VSLOC")
.Where(e => (string)e.Parent.Element("id") == "G554")
.FirstOrDefault();
if (node != null) node.Remove();
xDoc.Save(fname);

Related

Remove xml node -Xelement

I have a xml file from which only specific nodes have to be removed. The node name will be given as input from the user. How to remove the specific nodes which have requested from the user?
<Customers>
<Customer>
<id>michle</id>
<address>newjersy</address>
</Customer>
<Customer>
<id>ann</id>
<address>canada</address>
</Customer>
</Customers>
I have tried
var customer = new XElement("customer",
from o in customers
select
new XElement("id", id),
new XElement("address", address)
);
Customer will contain a new node
<Customer>
<id>ann</id>
<address>canada</address>
</Customer>
doc.Element("customers").Elements(customer).ToList().Remove();
but this is not working. How can I remove the element from the xml?
Tom,
Try this...
private static void RemoveNode(string sID)
{
XDocument doc = XDocument.Load(#"D:\\Projects\\RemoveNode.xml");
var v = from n in doc.Descendants("Customer")
where n.Element("id").Value == sID
select n;
v.Remove();
doc.Save(#"D:\\Projects\\RemoveNode.xml");
}
This removed one node when I called it using
RemoveNode("michle");
Hope this helps.
Your main mistake is that you are creating new nodes that not attached with the source document instead of retrieving existed nodes from it.
You can use article "Removing Elements, Attributes, and Nodes from an XML Tree" on MSDN as a guideline to manipulating XML data.
For example, use XNode.Remove() method to delete one node from the tree or Extensions.Remove<T>(this IEnumerable<T> source) where T : XNode to remove every node in the source collection of nodes:
doc.Descendants("Customer")
.Where(x => x.Element("id").Value == id)
.Remove();
But you also need to save document via Save method after that for commit your changes:
doc.Save();
You can remove this way based on id
xdoc.Descendants("Customer")
.Where(x => (string)x.Element("id") == "michle")
.Remove();

Delete Selected TreeNode from Xml C#

I am working on Winform using C#, i have populated Treeview from XML and need to Delete the treeview Selected Node ( Run time). Delete Event:
TreeNode node = treeView1.SelectedNode;
if (treeView1.SelectedNode != null)
{
if (treeView1.SelectedNode.Parent == null)
treeView1.SelectedNode.Remove();
else if (treeView1.SelectedNode.Parent.Nodes.Count == 1)
treeView1.SelectedNode.Parent.Remove();
else
treeView1.SelectedNode.Remove();
}
XDocument doc = XDocument.Load("menu.xml");
var xElement = (from q in doc.Elements("root")
where q.Attribute("quick").Value == node.Tag.ToString()
select q);
foreach (var a in xElement)
a.Remove();
doc.Save("menu.xml");
Where my XML file is:
<?xml version="1.0" encoding="utf-8"?>
<root>
<quick title="1st Node" />
</root>
I understand that from the above xml you want to delete Node:quick. Try this:
doc.Root.Descendants("quick").FirstOrDefault().Remove();
This works if you have only one 'quick' node.
But there can be multiple 'quick' nodes, so to get the selected node to match in xml, u need to match the unique attribute value of that node to selected node same attribute value.
doc.Root.Descendants("quick")
.First(x => x.Attribute("title").Value == node.Tag["title"].Tostring())
.Remove();

Find element with specific attribute in xml?

I have the following xml:
<?xml version="1.0" encoding="utf-8" ?>
<layout>
<menu name="Employees" url="Employees.aspx" admin="0">
</menu>
<menu name="Projects" url="Projects.aspx" admin="1">
</menu>
<menu name="Cases" url="Cases.aspx" admin="1">
</menu>
<menu name="CaseView" url="CaseView.aspx" admin="1" hidden="1" parent="Projects">
</menu>
<menu name="Management" url="" admin="1">
<item name="Groups" url="Groups.aspx" admin="1" parent="Management"/>
<item name="Statuses" url="Statuses.aspx" admin="1"/>
</menu>
</layout>
Here I have CaseView and Groups that both have a 'parent' attribute.
Currently I iterate like this:
IEnumerable<XElement> menus =
doc.Element("layout").Elements();
foreach (var menu in menus)
{
string name = menu.Attribute("name").Value;
string active = "";
string url = menu.Attribute("url").Value;
if(activePage == url)
{
active = "class=\"active\"";
}
...
What I want is:
if(activePage == url || ActiveIsChildOf(name, activePage))
{
active = "class=\"active\"";
}
Essentially this method needs to find if an element with activePage as its url attribute exists. If it does, see if it has a parent attribute; if it does, check if the parent == name.
Is there some way to find an element by attribute or something?
ex:
XElement e = doc.GetByAttribute("url",activePage)
Thanks
Since you are using Linq to XML, you can use Descendants method - it returns all child elements, not just the direct children. After that, you can use LINQ to filter the results.
XDocument doc;
string activePage;
var activeMenu = doc.Descendants("menu")
.FirstOrDefault(o => o.Attribute("url").Value == activePage);
You might need to check if o.Attribute("url") does not return null (it does when the attribute does not exist) if you cannot guarantee that the source XML does not have such attribute for all menu elements.
You can also skip the argument to Descendants() to check all elements - in your sample data that would allow you to check both menu and item elements. For example:
var activeMenu = doc.Descendants()
.Where(o => o.Name == "menu" || o.Name == "item")
.FirstOrDefault(o => o.Attribute("url").Value == activePage);
If xpath is too cryptic, you can use LINQ:
IEnumerable<XElement> hits =
(from el in XMLDoc.root.Elements("item")
where (string)el.Attribute("url") == activePage
select el);
or like this:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements("item").FirstOrDefault(e => ((string)e.Attribute("url")) == activePage);
if(null != xele )
{
// do something with it
}
And you probably want it case-insensitive:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements("item").FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals((string)e.Attribute("url") , activePage));
if(null != xele )
{
// do something with it
}
If you want both menu and item, use this:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements().FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals((string)e.Attribute("url") , activePage));
if(null != xele )
{
// do something with it
}
You can simply use xPath. It's a query language for XML.
You can formulate something like this :
var xDoc = new XmlDocument();
xDoc.Load("XmlFile.xml");
//Fetch your node here
XmlNode = xDoc.SelectSingleNode(/layout/menu[#url='activepage'][1]);
It returns a set of node and the index 1 is to get the first node of the given set.
You can always use xDoc.SelectNodes if you want all the matching nodes.
Since you are using LINQ you can simply include System.Xml.XPath and select nodes with XPathSelectElement or XPathSelectElements.
You can do that with XPath:
doc.SelectNodes("//*[#url='" + activePage + "']")
It will return all document items that have activePage as url attribute.
A case insensitive search example, converting xml to a dictionary:
Dim expandos = XDocument.Parse(Request("Xml")).Root.Elements.Select(
Function(e)
Dim expando As Object = New ExpandoObject,
dic = e.Attributes.ToDictionary(Function(a) a.Name.LocalName, Function(a) a.Value,
StringComparer.InvariantCultureIgnoreCase)
expando.PedidoId = dic("PedidoId")
expando.FichaTecnicaModeloId = dic("FichaTecnicaModeloId")
expando.Comodo = dic("Comodo")
expando.Cliente = dic("Cliente")
Return expando
End Function)

How can I get a node from an XML document based on its attribute value and into an XElement object?

I need to inject some XML into a pre-existing XML file under a certain node. Here is the code I have to create my XML:
//Define the nodes
XElement dataItemNode = new XElement("DataItem");
XElement setterNodeDisplayName = new XElement("Setter");
XElement setterNodeOU = new XElement("Setter");
//Create the tree with the nodes
dataItemNode.Add(setterNodeDisplayName);
dataItemNode.Add(setterNodeOU);
//Define the attributes
XAttribute nameAttrib = new XAttribute("Name", "OrganizationalUnits");
XAttribute displayNameAttrib = new XAttribute("Property", "DisplayName");
XAttribute ouAttrib = new XAttribute("Property", "OU");
//Attach the attributes to the nodes
setterNodeDisplayName.Add(displayNameAttrib);
setterNodeOU.Add(ouAttrib);
//Set the values for each node
setterNodeDisplayName.SetValue("TESTING DISPLAY NAME");
setterNodeOU.SetValue("OU=funky-butt,OU=super,OU=duper,OU=TMI,DC=rompa-room,DC=pbs,DC=com");
Here is the code I have so far to load up the XML document and try to get the node that I need to insert my XML under:
//Load up the UDI Wizard XML file
XDocument udiXML = XDocument.Load("UDIWizard_Config.xml");
//Get the node that I need to append to and then append my XML to it
XElement ouNode = THIS IS WHAT I DONT KNOW HOW TO DO
ouNode.Add(dataItemNode);
Here is the XML from the existing document I am trying to work with:
<Data Name="OrganizationalUnits">
<DataItem>
<Setter Property="DisplayName">TESTING DISPLAY NAME</Setter>
<Setter Property="OU">OU=funky-butt,OU=super,OU=duper,OU=TMI,DC=rompa-room,DC=pbs,DC=com</Setter>
</DataItem>
I have multiple nodes that with the name of "Data", but I need to get the node that is , and I don't know how. Just learning how to use XML with C#.
Thank you.
This will get the first Data node with Name attribute matching OrganizationalUnits:
var ouNode = udiXML
.Descendants("Data")
.Where(n => n.Attribute("Name") != null)
.Where(n => n.Attribute("Name").Value == "OrganizationalUnits")
.First();
If your document might contain Data nodes without Name attribute, extra check for null might be necessary.
Note that you can achieve the same result with XPath (this will select root Data node, you can get DataItem node using Element method):
var ouNode = udiXML.XPathSelectElement("//Data[#Name = 'OrganizationalUnits']");

LINQ: How to return all child elements?

For an application I am working on, I have to display data from an XML File. There's a few transformations being done, but eventually the end result will be displayed in a treeview. When a user then clicks on a node, I want to pop up the details in a listview.
When no node has been selected, I basically use LINQ to grab the details of the first item I encounter.
Here's a simplified version of my XML
<root>
<parent label="parent1">
<child label="child1">
<element1>data</element1>
<element2>data</element2>
...
</child>
<child label="child2">
<element1>data</element1>
<element2>data</element2>
...
</child>
</parent>
</root>
And here's the code used to grab it (After selecting the parent-node that the treeview has been set to by means of an XPAthSelectStatement):
protected void listsSource_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{
XElement rootElement = XElement.Load(MapPath(TreeSource.DataFile));
rootElement = rootElement.XPathSelectElement("//parent[#label='parent1']");
XElement parentElement;
parentElement = rootElement;
var query = (from itemElement in parentElement.Descendants("child")
select new
{
varElement1 = itemElement.Element("element1").Value,
varElement2 = itemElement.Element("element2").Value,
...
}).Take(1);
e.result = Query;
}
This works a treat, and I can read out the varElement1 and varElement2 values from there. However, when I try and implement a similar mechanism for when the user actually did select a node, I seem to run into a wall.
My approach was to use another XPatchSelectStatement to get to the actual node:
parentElement = rootElement.XPathSelectElement("//child[#label='" + tvwChildren.SelectedNode.Text + "']");
But I am kind of stumped on how to now get a proper LINQ query built up to read in all elements nested under the child node. I tried using parentElement.Elements(), but that was yielding an error. I also looked at using Nodes(), but with similar results.
I suppose I could use a foreach loop to access the nodes, but then I'm not sure how to get the results into a LINQ query so I can return the same e.Result = query back.
I'm fairly new to LINQ, as you might have guessed, so any hints would be very much appreciated.
Here's the query that will give you the child element (given that there is only one child element with the specified label):
var childElement = rootNode.Descendants("child")
.Single(e=>e.Attribute("label").Value == "child1");
If you have more than one child elements with label="child1" but those elements are under different parent elements you can use the same approach to get first the parent element and then the child element.
Having the above, you can use this query to get all element nodes under the child node:
var elements = childElement.Descendants().Select(e=>e.Value);
I think data binding is much easier in this case.
XDocument doc = XDocument.Load(filePath);
if (doc.Root == null)
{
throw new ApplicationException("invalid data");
}
tvwChildren.Source=doc;
But if you want in this way hope following one helps(not the exact solution)
XElement root = XElement.Load("Employees.xml");
TreeNode rootNode = new TreeNode(root.Name.LocalName);
treeView1.Nodes.Add(rootNode);
foreach(XElement employee in root.Elements())
{
TreeNode employeeNode = new TreeNode("Employee ID :" + employee.Attribute("employeeid").Value);
rootNode.Nodes.Add(employeeNode);
if (employee.HasElements)
{
foreach(XElement employeechild in employee.Descendants())
{
TreeNode childNode = new TreeNode(employeechild.Value);
employeeNode.Nodes.Add(childNode);
}
}
}
And you can try Resharper tool for create better linq statements. It shows possible ones and you can easily convert each for,foreach loops into linq statements.
I'm not entirely sure I understand what you're trying to do, but it sounds like it could be this:
var data =
from p in xml.Root.Elements("parent")
where p.Attribute("label").Value == "parent1"
from c in p.Elements("child")
where c.Attribute("label").Value == "child2"
from d in c.Elements()
select d.Value;
Let me know if that helps.
Using this Xml library you can write your XPath like:
XElement child = rootElement.XPathElement(
"//parent[#label={0}]/child[#label={1}]", "parent1", "child2");

Categories

Resources