Select Parent XML(Entire Hierarchy) Elements based on Child element values LINQ - c#

I have the following XML and query through the ID,how do get the Parent Hierarchy
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>
In this if i query(Id = 4) and find out the Parent elements using Linq in the particular element how to get the following output with Hierarchy.
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4"/>
</Child3>
</Child2>
</Child1>
</Child>
Thanks In Advance.

Assume you want just one node parent tree:
string xml = #"<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
TextReader tr = new StringReader(xml);
XDocument doc = XDocument.Load(tr);
IEnumerable<XElement> myList =
from el in doc.Descendants()
where (string)el.Attribute("Id") == "4" // here whatever you want
select el;
// select your hero element in some way
XElement hero = myList.FirstOrDefault();
foreach (XElement ancestor in hero.Ancestors())
{
Console.WriteLine(ancestor.Name); // rebuild your tree in a separate document, I print ;)
}
To search for every element of your tree iterate retrieve the node with the select query without the where clause and call the foreach for every element.

Based on the sample XML provided, you could walk up the tree to find the parent node once you've found the node in question:
string xml =
#"<Child>
<Child1 Id='1'>
<Child2 Id='2'>
<Child3 Id='3'>
<Child4 Id='4'>
<Child5 Id='5'/>
<Child6 Id='6'/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
var doc = XDocument.Parse( xml );
// assumes there will always be an Id attribute for each node
// and there will be an Id with a value of 4
// otherwise an exception will be thrown.
XElement el = doc.Root.Descendants().First( x => x.Attribute( "Id" ).Value == "4" );
// discared all child nodes
el.RemoveNodes();
// walk up the tree to find the parent; when the
// parent is null, then the current node is the
// top most parent.
while( true )
{
if( el.Parent == null )
{
break;
}
el = el.Parent;
}

In Linq to XML there is a method called AncestorsAndSelf on XElement that
Returns a collection of elements that contain this element, and the
ancestors of this element.
But it will not transform your XML tree the way you want it.
What you want is:
For a given element, find the parent
Remove all elements from parent but the given element
Remove all elements from the given element
Something like this in Linq (no error handling):
XDocument doc = XDocument.Parse("<xml content>");
//finding element having 4 as ID for example
XElement el = doc.Descendants().First(el => el.Attribute("Id").Value == "4");
el.RemoveNodes();
XElement parent = el.Parent;
parent.RemoveNodes();
parent.Add(el);
[Edit]
doc.ToString() must give you what you want as a string.
[Edit]
Using RemoveNodes instead of RemoveAll, the last one also removes attributes.
Removing nodes from the chosen element too.

I found the following way
XElement elementNode = element.Descendants()
.FirstOrDefault(id => id.Attribute("id").Value == "4");
elementNode.RemoveNodes();
while (elementNode.Parent != null)
{
XElement lastNode = new XElement(elementNode);
elementNode = elementNode.Parent;
elementNode.RemoveNodes();
elementNode.DescendantsAndSelf().Last().AddFirst(lastNode);
}
return or Print elementNode.

Related

C# Find Attribute in XML with another Attribute with Descendants

First I want to find and select the PID"5678" from the <Tool>. With help of this PID, i want to find and select the ID"5678" from the <Parent>. The PID and the ID are the same value, but I have to find it from the <Tool> first.
At the moment I have following Code, to select the first PID. How can I "copy" this value and search with them the Attribute "ID"?
List<string> urls = xmldoc2.Descendants("PID").Select(x => x.Attribute("5678").Value).ToList();
<Tools>
<Tools>
<Tool>
<ID>1234</ID>
<PID>5678</PID>
<Name>Test</Name>
</Tool>
</Tools>
<Type>
<Parent>
<ID>5678</ID>
<PID>9999</PID>
<Name>Test2</Name>
</Parent>
</Type>
</Tools>
Notice that your Xml has multiple Root nodes - which does not work well.
So wrap it into single parent node (i.e. "Root" in below example)
Something of this sort should help you.
string xmlData = #"... Your Xml here....";
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlData);
var pidNodes = xmlDoc.SelectNodes("//Root/Tools/Tools/Tool/PID");
foreach(XmlNode node in pidNodes)
{
var typeNodeForPid = xmlDoc.SelectSingleNode(string.Format("//Root/Type/Parent[ID = '{0}']", node.InnerText));
}

Formatting an elements text using descendants of XElement

Im wondering how to select a specific element from the heirarchy so that I can format its text.
In the example below I would like to format the specific element to remove the time portion of the date, but I am also looking for a way to format any of the elements for example to add a currency symbol to the text between each price tag.
My example
<orders>
<order>
<type> tools </type> //I would like the ability to select this element
<text> screwdriver </text>
<id> 100981 </id>
<price> 5.00 </price>
<date> 01/01/15 12:51:36 </date>
</order>
<order>
<type> uniform </type>
<text> boots </text>
<id> 100546 </id>
<price> 25.00 </price>
<date> 12/01/15 15:30:41 </date>
</order>
</orders>
What I have so far
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );// set the first letter of each output to be uppercase
}
}
What I have tried
foreach (XElement element in doc.Descendants())
{
var nodes = element.Nodes().Where(p => p.NodeType == XmlNodeType.Text);
if( element.Descendants() == element.Element("date"))
{
element.Value = Convert.ToDateTime(element.Value).ToShortDateString();
}
foreach (XText node in nodes)
{
node.Value = FirstLetterToUpper( node.Value );
}
}
I have some XML experience but have never worked with XElement before.
I've been searching SO for a while now but cant find what Im looking for. The answers below are some of the suggested answers from typing this question but they dont provide a solution as the XML elements are generated dynamically in a loop.
XElement node with text node has its formatting ignored
string.Format in XElement not formatting
Any help with this would be great as I have not attempted this before. Thanks.
You can get the text node's parent using Parent property and check it's name:
foreach (XText node in doc.DescendantNodes()
.Where(x => NodeType == XmlNodeType.Text))
{
if(node.Parent.Name == "date") { ... }
if(node.Parent.Name == "price") { ... }
}
BTW, don't forget the save the document using XDocument.Save method after you made the changes.
I usually create one class to represent the xml and send yo the instance of object with correct type. for Example convert.ToDecimal(node.innerText)

Unable to remove root node from an xml document using linq to xml c#

Actullay, I need to get all elements except root node from first xml document and so that I could insert them as child nodes to an element(that has same name as a previous doc's root name) in a new document.
So I have tried various ways to achieve it, one of them is removing the root node of first and then trying to add elements to a new one's as given below:
I have tried the following but could not achieve it.
XDocument testDoc = XDocument.Parse(Mydocument);
testDoc.Descendants().Where(e => e.Name.LocalName == "rootName").Select(m=>m).Single().Remove();
var resultDoc = testDoc;
The above code is giving me an empty "{}" result.
my xml document looks something like the below one's:
<rootName xsi:schemaLocation="" xmlns:xsi="" xmlns="">
<main>
<child>
</child>
<anotherchild>
</anotherchild>
</main>
</rootName>
And another way is getting all the elements of first document as the following:
var resultDoc = testDoc.Descendants(ns + "rootName").Elements();
the above statement is giving me the list of elements in the "testDoc" which
I need to do something like below, I am clueless:
<AnotherDocument xsi:schemaLocation="" xmlns:xsi="" xmlns="">
<firstNode>
<rootName>
<main>
<child>
</child>
<anotherchild>
</anotherchild>
</main>
</rootName>
</firstNode>
Please let me know how to insert those elements in a new document as above if I am correct else let me know the way to resolve this issue.
Thanks in advance.
You can replace content of rootName element in another document with elements from first document root:
var xDoc = XDocument.Parse(Mydocument);
var anotherXDoc = XDocument.Load("anotherdata.xml");
XNamespace ns = "http://..."; // your xml namespance
var rootName = anotherXDoc.Descendants(ns + "rootName").First();
rootName.ReplaceNodes(xDoc.Root.Elements());
By this page_nodes gets all nodes now you can used all node by for each loop
var page_nodes = from p in xdoc.Descendants.Where(e => e.Name.LocalName == "rootName").Select(m=>m).Single().Remove() select p;
foreach (var page_node in page_nodes)
{
//Do stuff
}
Wouldn't removing a root node, remove all its child nodes as well? The result you are getting is to be expected I think. You should probably get all the children of the root and copy them to your new document.

Modify Node Value C# with ID

Here is my XML :
<?xml version="1.0" encoding="utf-8" ?>
<Selection>
<ID>1</ID>
<Nom>Name 1</Nom>
<DateReference>0</DateReference>
<PrefixeMedia>Department</PrefixeMedia>
<FormatExport>1630</FormatExport>
<TraceAuto>Oui</TraceAuto>
<SubID></SubID>
</Selection>
<Selection>
<ID>2</ID>
<Nom>Name 1</Nom>
<DateReference>0</DateReference>
<PrefixeMedia>Department</PrefixeMedia>
<FormatExport>1630</FormatExport>
<TraceAuto>1</TraceAuto>
<SubID>1</SubID>
</Selection>
My problem is I would like to modify for example the node content of <Nom>Name 1</Nom> which is located in <Selection></Selection> which have <ID>1</ID> (Search by ID)
I'm using XElement and XDocument to do simple search but I need some help to solve this problem above. (Developpment on SilverLight
Best Regards.
Another way to do this is using XmlDocument:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"\path\to\file.xml");
// Select the <nom> node under the <Selection> node which has <ID> of '1'
XmlNode name = xmlDoc.SelectSingleNode("/Selection[ID='1']/Nom");
// Modify the value of the node
name.InnerText = "New Name 1";
// Save the XML document
xmlDoc.Save(#"\path\to\file.xml");
If you don't know how to get at the correct <Nom> node to update, the trick is to first select a <Selection> node that contains the correct <ID> node, then you can get that <Nom> node.
Something like:
XElement tree = <your XML>;
XElement selection = tree.Descendants("Selection")
.Where(n => n.Descendants("ID").First().Value == "1") // search for <ID>1</ID>
.FirstOrDefault();
if (selection != null)
{
XElement nom = selection.Descendants("Nom").First();
nom.Value = "Name one";
}
Note 1: By using Descendants("ID").First() I expect every Selection node to contain an ID node.
Note 2: And every Selection node contains a Nom node
Note 3: Now you still have to store the whole XML, if that's what you need.

Search XML doc with LINQ

I have an xml doc similar to this:
<Root>
<MainItem ID="1">
<SubItem></SubItem>
<SubItem></SubItem>
<SubItem></SubItem>
</MainItem>
<MainItem ID="2">
<SubItem></SubItem>
<SubItem></SubItem>
<SubItem></SubItem>
</MainItem>
...
</Root>
I want to return the whole of the MainItem element based on the value of attribute ID.
So effectively if Attribute ID is equal to 2, then give me that MainItem element back.
I can't work out how to do this with LINQ.
There seems to be a load of information on google, but I just can't quite seem to find what I'm looking for.
Little help ?
TIA
:-)
It could be something like this:
XDocument doc = XDocument.Load("myxmlfile.xml");
XElement mainElement = doc.Element("Root")
.Elements("MainItem")
.First(e => (int)e.Attribute("ID") == 2);
// additional work
How about this:
// load your XML
XDocument doc = XDocument.Load(#"D:\linq.xml");
// find element which has a ID=2 value
XElement mainItem = doc.Descendants("MainItem")
.Where(mi => mi.Attribute("ID").Value == "2")
.FirstOrDefault();
if(mainItem != null)
{
// do whatever you need to do
}
Marc
I changed your XML slightly to have values:
<?xml version="1.0"?>
<Root>
<MainItem ID="1">
<SubItem>value 1</SubItem>
<SubItem>val 2</SubItem>
<SubItem></SubItem>
</MainItem>
<MainItem ID="2">
<SubItem></SubItem>
<SubItem></SubItem>
<SubItem></SubItem>
</MainItem>
</Root>
And with this LINQ:
XDocument xmlDoc = XDocument.Load(#"C:\test.xml");
var result = from mainitem in xmlDoc.Descendants("MainItem")
where mainitem.Attribute("ID").Value == "1"
select mainitem;
foreach (var subitem in result.First().Descendants())
{
Console.WriteLine(subitem.Value);
}
Console.Read();
From here: How to: Filter on an Attribute (XPath-LINQ to XML)
// LINQ to XML query
IEnumerable<XElement> list1 =
from el in items.Descendants("MainItem")
where (string)el.Attribute("ID") == "2"
select el;
// XPath expression
IEnumerable<XElement> list2 = items.XPathSelectElements(".//MainItem[#ID='2']");

Categories

Resources