C# get XElement attributes from XDocument with treeview path - c#

I'm trying to write a winforms application that displays any opened XML files nodes (and only it's nodes) in TreeView and displays the attributes of the selected node separately (by selected i mean selected in the TreeView) (displayed in a listbox for example). I tried to achieve this by using the following code but it throws an exception saying:'family\parent' has an invalid token.
private void TView__AfterSelect(object sender, TreeViewEventArgs e)
{
var doc = XDocument.Load(businessLayer.InputFilepath);
XElement myElement = doc.Root.XPathSelectElement(TView_.SelectedNode.FullPath);
try
{
foreach (var attribute in myElement.Attributes())
{
listBox1.Items.Add(attribute.Value);
}
}
catch (Exception)
{
}
}
I'm using the following XML file:
<?xml version="1.0" encoding="utf-8" ?>
<family>
<parent>
<id>grandfather</id>
<parent>
<id>father</id>
<parent>
<id>brother</id>
<child>
<id>niece</id>
</child>
</parent>
<parent>
<is>me</is>
<child>
<id>son</id>
</child>
<child>
<id>dauhter</id>
</child>
</parent>
<child>
<id>sister</id>
</child>
</parent>
<parent>
<id>uncle</id>
<parent>
<id>cousin sister</id>
<child>
<id>second cousin</id>
</child>
</parent>
<child>
<id>cousin brother</id>
</child>
</parent>
</parent>
</family>
I have no clue about what else i could try so any help is appreciated.

To avoid that exception you have to replace every double backslash to a slash in the path of you XML file.
string path = treeView1.SelectedNode.FullPath.Replace('\\', '/');
XElement myElement = doc.XPathSelectElement(path);

Related

XPath 1.0 select siblings with namespaces

I have the following xml file
<root xmlns="http://mynamespace">
<parent>
<first>text</first>
<second>more</second>
</parent>
<parent>
<first>2</first>
<second>3</second>
</parent>
<parent>
<first>aa</first>
<second>bb</second>
</parent>
</root>
I'm trying to get first and second children of parent.
C# seems to have problems with the following code (the error is on the last line):
var rawXml = #"<root xmlns=""http://mynamespace"">
<parent>
<first>text</first>
<second>more</second>
<third>hello</third>
</parent>
<parent>
<first>2</first>
<second>3</second>
<parent>
<first>a</first>
<second>b</second>
</parent>
</parent>
<parent>
<first>aa</first>
<second>bb</second>
</parent>
</root>";
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(rawXml);
var ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("m", "http://mynamespace");
var nav = xmlDoc.CreateNavigator();
var parents = nav.Select("//m:parent", ns);
Console.Write($"Got {parents.Count} parents.");
// this does not work
// error: Expression must evaluate to a node-set.
//var siblings = nav.Select("//m:parent/(m:first|m:second)", ns);
// but this does
var siblings = nav.Select("//m:parent/m:first|//m:parent/m:second", ns);
Console.Write($"Got {siblings.Count} children.");
Am I missing something? Is the first XPath expression wrong?
Is the first XPath expression wrong?
Yes, it's not valid XPath 1.0 syntax. You can't have a ( after a / in XPath 1.0.
You can achieve what you're trying to do, without repeating any node names, by using this path:
/m:root/m:parent/*[self::m:first or self::m:second]
Side note: avoid using // unless you have a specific reason to use it. It's bad for performance.

Linq to XML select descendent of descendant each with specific attribute

I have found many articles on getting a descendant with a specific attribute, but I can't seem to find anything on selecting multiple descendants with different attributes with LINQ to XML. So from the following example, I need to select all the Grandchildren where Parent name = Ken AND Child name = Lorna. Potentially, I need to have up to 4 AND clauses as my real XML is deeper than the example below.
I can code selecting all children of Ken, but can't find an example to go deeper than that.
Any help is greatly appreciated
<?xml version="1.0" encoding="UTF-8"?>
<FamilyTree>
<Parent name="Ken">
<Child name="Lorna">
<Grandchild name="Andrew"/>
<Grandchild name="Brian"/>
</Child>
<Child name="Mike">
<Grandchild name="Ann"/>
<Grandchild name="Beth"/>
</Child>
</Parent>
<Parent name="Norma">
<Child name="Owen">
<Grandchild name="Charles"/>
</Child>
<Child name="Peter">
<Grandchild name="Charlotte"/>
</Child>
</Parent>
<Parent name="Quinn">
<Child name="Robert">
<Grandchild name="Debbie"/>
<Grandchild name="Eric"/>
</Child>
<Child name="Susan">
<Grandchild name="Frank"/>
</Child>
</Parent>
</FamilyTree>
There are multiple options here, but I'd suggest the simplest thing is just to check each Grandchild:
var grandchildren = doc
.Descendants("Grandchild")
.Where(x => (string) x.Parent.Parent.Attribute("name") == "Ken" &&
(string) x.Parent.Attribute("name") == "Lorna");
Or you could find all the relevant Child elements and then retrieves their children:
var grandchildren = doc
.Descendants("Child")
.Where(x => (string) x.Parent.Attribute("name") == "Ken" &&
(string) x.Attribute("name") == "Lorna")
.Elements("Grandchild");

LINQ to XML: How to get all elements by value

I'm trying to get all elements with a given value, "John", from an xml document.
Is this possible with LINQ to XML?
What I want to achieve is to replace all "John" values with "Wayne". I know this can easily be done with xslt, but I need to do this by code.
My XML:
<Root>
<Parents>
<Parent>
<Name>John</Name>
<Age>18</Age>
</Parent>
<Parent>
<Name>John</Name>
<Age>25</Age>
</Parent>
<Parent>
<Name>Peter</Name>
<Age>31</Age>
</Parent>
</Parents>
</Root>
I have tried this:
XmlDocument doc = new XmlDocument();
doc.Load(#"C:/Temp/test.xml");
var elements = doc.Elements().Where(w => w.Value == "John");
foreach (var element in elements)
{
element.Value = "Wayne";
}
You may use System.Xml.Linq.XDocument. It's more easy to work with.
XDocument doc = XDocument.Load(your file path);
var elements = doc.Descendants("Name").Where(i => i.Value == "John");
foreach (var element in elements)
{
element.Value = "Wayne";
}
doc.Save(your save file path);
Here is the output:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Parents>
<Parent>
<Name>Wayne</Name>
<Age>18</Age>
</Parent>
<Parent>
<Name>Wayne</Name>
<Age>25</Age>
</Parent>
<Parent>
<Name>Peter</Name>
<Age>31</Age>
</Parent>
</Parents>
</Root>
Here is an approach that will get all elements with the value John, regardless of what element (although only at the same level; you'd have to modify it to look at different levels too; you could use the Descendants approach described previously):
XDocument doc = XDocument.Load(#"C:\temp\test.xml");
var ns = doc.Root.GetDefaultNamespace();
var elements = doc.Element(ns + "Root").Element(ns + "Parents").Elements(ns + "Parent").Elements().Where(w => w.Value == "John");
foreach (var element in elements)
{
element.Value = "Wayne";
}
var stream = new FileStream(#"C:\temp\test.xml", FileMode.Create);
doc.Save(stream);

group node based on xpath predicate

I have strings of xpath like
/root/parent/child
/root/parent/child[1]
/root/parent/child[2]
In the c# code i am checking if the xpath exist in XmlDocument and cloning it like this
//Get the parent node of the node to be cloned
XmlNode NodeTobeCloned = xmlDoc.SelectSingleNode(oRow[0].ToString());
XmlNode DuplicateNode = NodeTobeCloned.CloneNode(true);
DuplicateNode.InnerText = sValue;
//Insert the node after the last child of a commoon parent
NodeTobeCloned.ParentNode.AppendChild(DuplicateNode);
I am getting a result like
<root>
<parent>
<child/>
<child/>
<child/>
</parent>
</root>
I want a result like
<root>
<parent>
<child/>
</parent>
<parent>
<child/> -- [1] -predicates elements
</parent>
<parent>
<child/> -- [2] -predicates elements
</parent>
</root>
how can i append to xmldocument using c#
thank you

XPath for selecting a node under a specific node with zero or more nodes in between

My Xml looks something like this
<root>
<parent name="Iam">
<child1 name="123">
<toy name="wii">
</toy>
</child>
</parent>
<parent name="Iam">
<toy name="wii">
</toy>
</parent>
<parent name="Sam">
<child1 name="999">
<toy name="xbox">
</toy>
</child>
</parent>
</root>
I need to select all <toy> nodes with name="wii" under <parent> with name="Iam". Notice that <toy> can be direct child of <parent>or a grandchild of <parent>(under <child>)
<child>node can have 0 or more cardinality.
I tried using this xpath /parent[#name='Iam']/*/toy[#name='wii'] with XPathNavigator.
`XPathNodeIterator nodeIter = schemaNavigator.Select(schemaXPath, namespaceMgr);`
It is obviously wrong since it fails.
I need xpath for selecting all nodes between <parent> and <toy> with 0 or more cardinality.
I cannot change the format of XML.
This seemed to worked for me:
string schemaXPath = "//parent[#name='Iam']//toy[#name='wii']";
XPathNavigator schemaNavigator = oXmlDocument.CreateNavigator();
XPathNodeIterator nodeIter = schemaNavigator.Select(schemaXPath, namespaceMgr);
while (nodeIter.MoveNext() == true)
{
Console.WriteLine(nodeIter.Current.Name);
}
Hopefully this is what you're looking for.
Cheers!

Categories