Replacing an xml element value in c# - 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.

Related

How to Get XML Element by It's 'Value' using XML C# SDK

I have this snippt of XML
<unit class="xxx.xxx.xxx" id="382">
<customId>000</customId>
<description>kg</description>
<key>22452</key>
<Description>Kilogramm</Description>
</unit>
how to get the node 'unit' or parnet of the key element using the value of an element. For instance
i have the value of key element above [22452] and it's Uniqe inside the xml-File.
what i am trying to do getting value of customid [000] of that specific tag.
what i did:
var doc = new XmlDocument();
doc.Load(stream); // stream from xml-file
var key = doc.SelectSingleNode(//key/[text()='" + 22452+ "']"); // that i am not sure about it.
var customId = key.InnerText("customId");
For this kind of query you could either find the node and than navigate to the parent.
Or use XPath:
var unitElemnt = doc.SelectSingleNode("//unit[key = '22452']");
(Assuming I've remembered the XPath to match an element's text content correctly.)
This gets a reference to the <unit> element, by using a relative path to the <key> element in the predicate of the XPath expression.
Generally better to avoid // in XPath for performance, but would need full document structure to do that.
For this you can use Linq to Xml queries.
XElement units = XElement.Load("./Test.xml");
XElement unit = (from item in units.Descendants("unit")
where item.Element("key").Value == "22455"
select item).FirstOrDefault();
string customId = unit.Element("customId").Value;
supposing your xml file look like :
<?xml version="1.0" encoding="utf-8"?>
<units>
<unit class="xxx.xxx.xxx" id="385">
<customId>003</customId>
<description>kg</description>
<key>22455</key>
<Description>Kilogramm</Description>
</unit>
<unit class="xxx.xxx.xxx" id="386">
<customId>004</customId>
<description>kg</description>
<key>22456</key>
<Description>Kilogramm</Description>
</unit>
</units>
for more reading check Microsoft Linq to Xml Docs

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.

Remove specific node from xml

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();

XElement with LINQ Select and optional elements

I am trying to display some date from some XML I get from an external service. I am using XElement and I try to use LINQ select to get my data.
var xElem = XElement.Load(HttpUtility.UrlPathEncode(url));
var books = (from pubs in xElem.Elements("result")
select new
{
Id = (string)pubs.Element("data").Element("id"),
Title = (string)pubs.Element("data").Element("title"),
Year = (string)pubs.Element("data").Element("year"),
Resources = (string)pubs.Element("data")
.Element("resource")
.Element("url")
.ElementValueNull(),
Authors= pubs.Element("data").Elements("person")
}).ToList();
foreach (var book in books)
{
// Put the string together with string builder....
foreach (var person in book.Authors)
{
//Get the authors
}
}
And of course I have made the class for ElementValueNull.
//This method is to handle if element is missing
public static string ElementValueNull(this XElement element)
{
if (element != null)
return element.Value;
return "";
}
//This method is to handle if attribute is missing
public static string AttributeValueNull(this XElement element, string attributeName)
{
if (element == null)
return "";
else
{
XAttribute attr = element.Attribute(attributeName);
return attr == null ? "" : attr.Value;
}
}
The problem is that the resource tag with it's elements are not always present. And if it isn't there it will skip the whole record. Is there any easy way of making it so that it will just make the Resources have the empty string returned from my class but still add the record still using a LINQ select?
EDIT with XML example:
<?xml version="1.0" encoding="UTF-8"?>
<tester xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://tester.no/xmlSchema/xsd/tester.xsd">
<generert>2014-12-01</generert>
<result>
<data>
<id>297474</id>
<person>
<id>11690</id>
<surname>Medel-Svensson</surname>
<firstname>Ronnie</firstname>
</person>
<title>Title 1</title>
<year>2009</year>
</data>
</result>
<result>
<data>
<id>807059</id>
<person>
<id>11690</id>
<surname>Bronskimlet</surname>
<firstname>Hallstein</firstname>
</person>
<person>
<id>328009</id>
<surname>Kroksleiven</surname>
<firstname>Jostein</firstname>
</person>
<person>
<id>328010</id>
<surname>Gassolini</surname>
<firstname>Ruffino</firstname>
</person>
<person>
<id>327990</id>
<surname>von Schnellfahrer</surname>
<firstname>Heinrich</firstname>
</person>
<title>Title 2</title>
<year>2010</year>
<resource>
<type>
<code>TEXT</code>
</type>
<url>http://www.example.com/</url>
</resource>
</data>
</result>
<result>
<data>
<id>1164653</id>
<person>
<id>11690</id>
<surname>Bergsprekken</surname>
<firstname>Mysil</firstname>
</person>
<title>Title 3</title>
<year>2014</year>
<resource>
<type>
<code>FULLTEKST</code>
</type>
<url>http://www.example.com/</url>
</resource>
</data>
</result>
</tester>
A couple of things:
if you use Element(..), then the result could be null. This may cause null reference exceptions if elements are missing in your path. A more elegant way to handle this would be to use sequences and return an element if present using SingleOrDefault()
Both XElement and XAttribute have a bunch of explicit type conversion operators built in. This means you can cast to string and various other primitives. As string is a reference type, it would return null if the XObject was null. Value types such as int would throw an exception in this case, though int? would not.
With this in mind, something like this should solve your problem. Note as 'data' is common to all, you can put this in the initial selector:
from pubs in xElem.Elements("result").Elements("data")
select new
{
Id = (string)pubs.Element("id"),
Title = (string)pubs.Element("title"),
Year = (string)pubs.Element("year"),
Resources = (string)pubs.Elements("resource")
.Elements("url")
.SingleOrDefault(),
Authors= pubs.Elements("person")
}

How to search using getelementid in xml

I want to search through XML Using GetElementId .
I have an XML file with attributes associated with each element. Elements name may defer but each element will have unique Id.
For example:
<root>
<secondRoot>
<Person UniqueID='A112' Name='Fred'><FeMale>I am Female</FeMale></Person>
<Person UniqueID='A111'><Male>I am male</Male></Person>
<Person SSN='A222' Name='Tom'/>
<Customer id='A111'/>
<Customer id='A222334444'/>
<Team members='A222334444 A333445555'/>
<Random/>
</secondRoot>
</root>
In the above XML i have UniqueID attribute associated with several elements. I want to search elements with UniqueID attribute . Eventhough i tried using following DTD its not sufficient.
<!DOCTYPE root [
<!ELEMENT root ANY>
<!ATTLIST Person UniqueID ID #REQUIRED>
]>
The Problem is UniqueID may occur in several elements attribute list.
I need to avoid a situation which i have to declare every occurances of elements that have UniqueID attribute in DTD.
Can Anyone suggest any idea for that?
Thanks
Try using LINQ to XML, no need for DTD.
// XML data
var xml = "<root><secondRoot><Person UniqueID='A112' Name='Fred'><FeMale>I am Female</FeMale></Person><Person UniqueID='A111'><Male>I am male</Male></Person> <Person SSN='A222' Name='Tom'/> <Customer id='A111'/> <Customer id='A222334444'/> <Team members='A222334444 A333445555'/> <Random/></secondRoot></root>";
var doc = XDocument.Parse(xml);
// Get all nodes that have UniqueID
var nodes =
from element in doc.Descendants()
where element.Attribute("UniqueID") != null
select element;
The nodes list will contain all nodes that have the UniqueID attribute.

Categories

Resources