Remove specific node from xml - c#

Here is my xml
<result>
<client></client>
<message></message>
<record>
<message></message>
</record>
</result>
I want to remove the "message" node which is right below "result"
when I tried to remove it by using below code:
responseXml.Descendants().Where(e => e.Name == "client" || e.Name == "message").Remove();
It is removing "message" which is under "record" but I don't want this.
I want to remove only "message" under "result"
Expected xml:
<result>
<client></client>
<record>
<message></message>
</record>
</result>
Please suggest me here.

Descendants() will return all elements (children and grand-children etc.), while Elements() will only return immediate children.
responseXml.Root.Elements().Where(e => e.Name == "message").Remove();
You could probably use the shorter .Element("message") syntax, but be aware that this method only returns the first element found. If you have more than one <message> it wont return/remove them all.

void Main()
{
string xml =#"
<result>
<client></client>
<message></message>
<record>
<message></message>
</record>
</result>";
XElement root = XElement.Parse(xml);
root.Element("message").Remove();
}
Removes exact element "message" directly under root node.

You can call Element(name), which returns a single XElement (calling Elements or Descendants returns a IEnumerable<XElement>):
responseXml.Root.Element("message").Remove();

Related

Using XDocument to convert selected node to string

I have the following XML sample.
<?xml version="1.0" encoding="utf-8"?>
<GlobalResponses>
<Filters>
<FilterId>11</FilterId>
<FilterId>5</FilterId>
<FilterId>10</FilterId>
</Filters>
<Responses>
<Response>
<Name>Bob</Name>
</Response>
<Response>
<Name>Jim</Name>
</Response>
<Response>
<Name>Steve</Name>
</Response>
</Responses>
</GlobalResponses>
Using XDocument, how can I get only the <Responses> parent and also child nodes, and convert them to a string variable. I looked at XDocument Elements and Descendants, but by calling oXDocument.Descendants("Responses").ToString(); didn't work.
Do I have to iterate over all of the XElements checking each one and then appending to a string variable ?
Function Descendants returns enumeration of XElement, so you need to select specific element.
If you want to get XML element with all the child nodes, you can use:
// assuming that you only have one tag Responses.
oXDocument.Descendants("Responses").First().ToString();
The result is
<Responses>
<Response>
<Name>Bob</Name>
</Response>
<Response>
<Name>Jim</Name>
</Response>
<Response>
<Name>Steve</Name>
</Response>
</Responses>
If you want to get child nodes and concatenate them to single string you can use
// Extract list of names
var names = doc.Descendants("Responses").Elements("Response").Select(x => x.Value);
// concatenate
var result = string.Join(", ", names);
The result is Bob, Jim, Steve
The Descendants() method takes input the element name and it will return you a collection of nodes and from those you then further need to get the elements you are interested in.
You can use linq with XDocument to extract the information. For example, the following code with extract the Name element value from each Response node and prints out :
var nodes = from response in Doc.Descendants("Response")
select response.Element("Name").Value;
foreach(var node in nodes)
Console.WriteLine(node);
Here above Doc.Descendants("Response") will fetch all the <Response> elements and then we are using response.Element("Name") to fetch the <Element> tag for each <Response> element and then using .Value property we get the value between the tag.
See this working DEMO fiddle.

C# LINQ Left Outer Join for XML doesn't work properly

I'm trying to do a Left Outer Join for two XMLs and get another XML(not a collection!) as an output, but LINQ's query 'into' seems to be extracting only values but not full elements with all the original tags and attributes.
My xml1 looks like this:
<tag>
<abc id="zxy">tiger</abc>
<abc id="zzz">rabbit</abc>
</tag>
My xml2:
<tag>
<aaa attr1="1" attr2="zzz">value1</aaa>
<aaa attr1="2" attr2="zxc">value2</aaa>
</tag>
My code in C#:
var que= from first in xml1
join second in xml2
on (string)first.Attribute(attr1) equals (string)second.Attribute(attr2) into temp
from tmpL in temp.DefaultIfEmpty()
orderby (string)first.Attribute(attr1)//, (string)tmpL.Attribute(attr2) -- this one isn't working because it's not an element
select new XElement("child", first, tmpL == null ? String.Empty : (string)tmpL);
var final= new XDocument(new XElement("parent", que));
This is what I get joining two above XMLs using that code:
<parent>
<child>
<abc id="zxy">tiger</abc>value1</child>
<child>
<abc id="zzz">rabbit</abc>value2</child>
</parent>
As you can see it's an invalid XML with value1 and value2 stick to the sibling elements, whereas they should be wrapped in their own original tags (with their original attributes): <aaa attr1="1" attr2="zzz">value1</aaa> and <aaa attr1="2" attr2="zxc">value2</aaa> correspondingly.
Thus I can't use .Attribute() and other things on them.
Also I can't just insert these values inside of newly created elements, because I need attributes from the original elements in xml2.
Can you please help me to get the following XML?
<parent>
<child>
<abc id="zxy">tiger</abc>
<aaa attr1="1" attr2="zzz">value1</aaa>
</child>
<child>
<abc id="zzz">rabbit</abc>
<aaa attr1="2" attr2="zxc">value2</aaa>
</child>
</parent>
"..but LINQ's query 'into' seems to be extracting only values but not full elements with all the original tags and attributes"
You actually got the XElements as expected, but then it was explicitly cast to string which cause only the string value retained, here :
select new XElement("child", first, tmpL == null ? String.Empty : (string)tmpL);
Remove casting and simply pass null instead of String.Empty when you want nothing to be the child of the newly created child element :
select new XElement("child", first, tmpL == null ? null : tmpL);
or simply pass tmpL no matter it is null or not, which yield in an equivalent yet cleaner statement :
select new XElement("child", first, tmpL);

Replacing an xml element value in c#

Here is my xml file data
<Persons>
<Person>
<Name>john</Name>
</Person>
<Employee>
<Detail>
<Firstname>john</FirstName>
</Detail>
</Employee>
<Student>
<FullName>john</FullName>
</Student>
</Persons>
I want to replace "john" to "danny" in all places.
How can I do this in c# ?
One possible way using XDocument :
var doc = XDocument.Load("path_to_xml_file.xml");
//select all leaf elements having value equals "john"
var elementsToUpdate = doc.Descendants()
.Where(o => o.Value == "john" && !o.HasElements);
//update elements value
foreach(XElement element in elementsToUpdate)
{
element.Value = "danny";
}
//save the XML back as file
doc.Save("path_to_xml_file.xml");
Notice that XElement.Value contains all text nodes within the element, concatenated.
The significance of this is, for example, considering your XML as input, not only <Name> has value of "john" but also <Person>. But we only want to update the leaf elements not the ancestors.
*) I assumed you didn't really meant to tag the question by xmldocument so this answer using the newer XML API XDocument, though using XmlDocument is also possible.

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

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.

Add an XML node to multiple parent nodes(which have same name)

I am trying to add an XML node to multiple parent nodes(which have same name). But it is only adding to the Last node of the XML and not in all.
input XML
<Record>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
</Record>
I want to add the Location element to every Emp node. My code is as below:
XmlNodeList xNodeList = doc.SelectNodes("/Record/Emp");
XmlElement xNewChild = doc.CreateElement("Location");
xNewChild.InnerText = "USA";
foreach (XmlNode item in xNodeList)
{
item.AppendChild(xNewChild);
}
doc.Save(path);
but I am getting output like this:
<Record>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
</Emp>
<Emp>
<ID>12</ID>
<Name>ABC</Name>
<Location>USA</Location>
</Emp>
</Record>
The Location element has not been added to the first Emp node.
Note: After debugging, I am able to find that the element has been added even for the first Emp node. But, in the saved XML file I am seeing this strange behavior.
Your xNewChild is a single new element. Simply adding it to multiple nodes will only serialize to the last node. A change like this should work:
XmlNodeList xNodeList = doc.SelectNodes("/Record/Emp");
foreach (XmlNode item in xNodeList)
{
XmlElement xNewChild = doc.CreateElement("Location");
xNewChild.InnerText = "USA";
item.AppendChild(xNewChild);
}
doc.Save(path);

Categories

Resources