XmlReader Innertext Problem - c#

I have an Xml document in which some of the elements look like this:
<rootNode attib1="qwerty" >
<subNode1>W</subNode1>
<subNode2>X</subNode2>
<subNode3>Y</subNode3>
<subNode4>Z</subNode4>
ABC
</rootNode>
My objective is to get "ABC" out of the above example. I tried looking at the InnerText (which returns "WXYZABC") and InnerXml and Value (which returns null) properties in the XmlElement class and bunch of properties in the XmlReader class too. Somehow I don't see an elegant way to extract the data I need.
Can someone please help me out?
Thanks in advance.

Have a go with this one:
string xml = #"<rootNode attib1=""qwerty"" >
<subNode1>W</subNode1>
<subNode2>X</subNode2>
<subNode3>Y</subNode3>
<subNode4>Z</subNode4>
ABC
</rootNode>";
var xElement = XElement.Parse(xml);
xElement.Elements().Remove();
xElement.Value.Dump();
What it does is remove all the known Elements and that leaves you with the text you are looking for.

Based on the excellent suggestion from #djechelon, I seem to have found a solution to this:
XmlDocument xdoc = new XmlDocument();
xdoc.Load(#"D:\Test.xml");
XmlElement xmlElement = xdoc.DocumentElement;
foreach (XmlNode node in xmlElement.ChildNodes)
if (node.NodeType == XmlNodeType.Text
&& !string.IsNullOrWhiteSpace(node.Value))
Console.WriteLine(node.Value.Trim());
The above uses the simple fact that the inner text is also an XmlNode as part of the ChildNodes collection of the XmlElement.
Thanks everyone for the great responses!

Try XmlElement.Value
Edit: This is the wrong approach as this will always return NULL on an element node.

Related

How to find particular node in XML and all of its child nodes?

This is my XML:
<?xml version="1.0"?>
<formatlist>
<format>
<formatName>WHC format</formatName>
<delCol>ID</delCol>
<delCol>CDRID</delCol>
<delCol>TGIN</delCol>
<delCol>IPIn</delCol>
<delCol>TGOUT</delCol>
<delCol>IPOut</delCol>
<srcNum>SRCNum</srcNum>
<distNum>DSTNum</distNum>
<connectTime>ConnectTime</connectTime>
<duration>Duration</duration>
</format>
<format>
<formatName existCombineCol="1">Umobile format</formatName> //this format
<delCol>billing_operator</delCol>
<hideCol>event_start_date</hideCol>
<hideCol>event_start_time</hideCol>
<afCombineName dateType="DateTime" format="dd/MM/yyyy HH:mm:ss"> //node i want
<name>ConnectdateTimeAFcombine</name>
<combineDate>event_start_date</combineDate>
<combineTime>event_start_time</combineTime>
</afCombineName>
<afCombineName dateType="DateTime" format="dd/MM/yyyy HH:mm:ss"> //node i want
<name>aaa</name>
<combineDate>bbb</combineDate>
<combineTime>ccc</combineTime>
</afCombineName>
<modifyPerfixCol action="add" perfix="60">bnum</modifyPerfixCol>
<srcNum>anum</srcNum>
<distNum>bnum</distNum>
<connectTime>ConnectdateTimeAFcombine</connectTime>
<duration>event_duration</duration>
</format>
</formatlist>
I want to find format with Umobile format then iterate over those two nodes.
<afCombineName dateType="DateTime" format="dd/MM/yyyy HH:mm:ss"> //node i want
<name>ConnectdateTimeAFcombine</name>
<combineDate>event_start_date</combineDate>
<combineTime>event_start_time</combineTime>
</afCombineName>
<afCombineName dateType="DateTime" format="dd/MM/yyyy HH:mm:ss"> //node i want
<name>aaa</name>
<combineDate>bbb</combineDate>
<combineTime>ccc</combineTime>
</afCombineName>
and list all the two node's child nodes. The result should like this:
ConnectdateTimeAFcombine,event_start_date,event_start_time.
aaa,bbb,ccc
How can I do this?
foreach(var children in format.Descendants())
{
//Do something with the child nodes of format.
}
For all XML related traversing, you should get used to using XPath expressions. It is very useful. Even if you could perhaps do something easier in your specific case, it is good practice to use XPath. This way, if your scheme changes at some point, you just update your XPath expression and your code will be up and running.
For a complete example, you can have a look at this article.
You can use the System.Xml namespace APIs along with System.Xml.XPath namespace API. Here is a quick algorithm that will help you do your task:
Fetch the text node containing the string Umobile format using the below XPATH:
XmlNode umobileFormatNameNode = document.SelectSingleNode("//formatName[text()='Umobile format']");
Now the parent of umobileFormatNameNode will be the node that you are interested in:
XmlNode formatNode = umobileFormatNameNode.ParentNode;
Now get the children for this node:
XmlNodeList afCombineFormatNodes = formatNode.SelectNodes("afCombineName");
You can now process the list of afCombineFormatNodes
for(XmlNode xmlNode in afCombineNameFormtNodes)
{
//process nodes
}
This way you can access those elements:
var doc = System.Xml.Linq.XDocument.Load("PATH TO YOUR XML FILE");
var result = doc.Descendants("format")
.Where(x => (string)x.Element("formatName") == "Umobile format")
.Select(x => x.Element("afCombineName"));
Then you can iterate the result this way:
foreach (var item in result)
{
string format = item.Attribute("format").Value.ToString();
string name = item.Element("name").Value.ToString();
string combineDate = item.Element("combineDate").Value.ToString();
string combineTime = item.Element("combineTime").Value.ToString();
}

Reading values from within an XNode

I have some code that is returning a XNode to me which looks like this:
<File>
<Component>Main</Component>
<Path>C:\Main\</Path>
<FileName>main.txt</FileName>
</File>
I need to have some C# code that will be able to pull out the value of Path for example (C:\Main). I know that if I was using an XML node I could do it like this:
String filePath = xmlNode["Path"].InnerText;
Does anybody know what the equivalent would be for an XNode? Any help is much appreciated!
Do you have to have it returning an XNode rather than an XElement? With an XElement it's simpler than with an XNode:
string filePath = fileElement.Element("Path").Value;
That will find the first Path element, and will throw a NullReferenceException if there aren't any. An alternative if you're happy to get null if there aren't any would be:
string filePath = (string) fileElement.Element("Path");
If you're really stuck with XNode, you'll either have to cast to XElement or possibly XContainer.
You can convert your XNode into XElement to access to its properties, my example:
XNode lastNode = myXElement.LastNode;
//if I want to get the 'ID' attribute
string id = (lastNode as XElement).Attribute("ID").Value;
Casting XNode to XElement works for the individual element to retrieve its value or attributes. But you won't be able to use myXelement.Elements("XXX") to get nested elements. For that you can use xmlNode.Nodes().
This should work:
var nodes = xmlNode.Nodes();//Get all nodes under 'File'
var fileNameNode = nodes.Where(el => ((XElement)el).Name.LocalName == "FileName")
.FirstOrDefault();
string filePath = ((XElement)fileNameNode).Value;
You may use this:
XElement xtr = XElement.Load("path/to/your/xml/file");
String filePath = xtr.Descendants("Path").Single().Value;
If you import System.Xml.XPath you can use XPathSelectElement like this on the XNode object:
String component = xmlNode.XPathSelectElement("Component");
String path = xmlNode.XPathSelectElement("Path");
String fileName = xmlNode.XPathSelectElement("FileName");
It depends on what convcrete objets is the abstract XNode. From XNode Class:
XNode is an abstract common base class for the following types:
XComment
XContainer: can be XDocument or XElement
XDocumentType
XProcessingInstruction
XText
So you need to check if you can cast it to an XDocument or an XElement, and use their methods, or use LINQ to XML:
For example:
Best way to query XDocument with LINQ?
Querying an XDocument vs. Querying an XElement (C#)
XNode xnode
string value = (xnode as XElement).Value;
we can get the value form XElement object so we need to cast XNode to XElement first
should work

XmlNode.SelectNode with multiple attribute

One of my nodes inmy xml file is as follows.
<LOGIN_ID NAME="Kapil">
<SEARCH_ID>Kapil Koli</SEARCH_ID>
<GUID>111</GUID>
<FIRST_NAME>Kapil</FIRST_NAME>
<LAST_NAME>Koli</LAST_NAME>
<EMAIL_ID>kapil#abc.co.in</EMAIL_ID>
<PASSWORD>abc123**</PASSWORD>
</LOGIN_ID>
The code I am using is -
XmlDocument document = new XmlDocument();
document.Load(_XmlFileName);
nodeList = document.SelectNode."USERS/LOGIN_ID[contains(SEARCH_ID,'Kapil')";
nodeList = document.SelectNode."USERS/LOGIN_ID[contains(EMAIL_ID,'kapil#abc.co.in')";
I want to use select node which will accept search_id and login_id as attributes to search?
If either search_id or email_id is wrong, I want to return null.
How could I do this?
thanks.
kapil.
USERS/LOGIN_ID[contains(SEARCH_ID,'Kapil') and contains(EMAIL_ID,'kapil#abc.co.in')]
should do the trick.

XML and the & character

I need to pass the & character inside an XML element, but its not liking it, here is a code sample:
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");
string item = "<field>http://mylink.com/page.aspx?id=1&disp=2</field>"
batch.InnerXml = item;
Its absolutely crucial I put this link inside, so does anyone know how to get around this?
Thank you
You need to escape it as &.
As people are saying, escaping the element will work. However, I find this a little cleaner:
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");
XmlElement field = doc.CreateElement("field");
string link = "http://mylink.com/page.aspx?id=1&disp=2"
field.InnerText = link;
batch.AppendChild(field);
Escape it: &.
string item = "<field>http://mylink.com/page.aspx?id=1&disp=2</field>";
Escape it as &. This is called HTML/XML Entities. See more information and list of others entities here and here.
The code should look like this:
string item = "<field>http://mylink.com/page.aspx?id=1&disp=2</field>"
you can use &
As others have pointed out, you can just use an & escape sequence. However, the more elegant approach is not to deal directly with the XML at all.
var doc = new XmlDocument();
var batch = doc.CreateElement("Batch");
var field = doc.CreateElement("field");
field.InnerText = "http://mylink.com/page.aspx?id=1&disp=2"
batch.Children.AppendChild(field);
No need to worry about escaping anything, this way. :)
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");
string item = "<field>http://mylink.com/page.aspx?id=1&disp=2</field>"
batch.InnerXml = item;
If you create the element with the Xml methods it will wrap everything up nicely for you. So use the CreateElement method again and set the InnerText property of the element to your link.
Use .InnerText rather than .InnerXml, and the XmlDocument instance will do all necessary encodings for you, like automatically escaping the & to &
The .InnerXml is used when the string you have is already valid xml which is not to be escaped, which is not the case here.

Best way test for XPath existence in an XML file?

Lately I've been using XPathDocument and XNavigator to parse an XML file for a given XPath and attribute. It's been working very well, when I know in advance what the XPath is.
Sometimes though, the XPath will be one of several possible XPath values, and I'd like to be able to test whether or not a given XPath exists.
In case I'm getting the nomenclature wrong, here's what I'm calling an XPath - given this XML blob:
<foo>
<bar baz="This is the value of the attribute named baz">
</foo>
I might be looking for what I'm calling an XPath of "//foo/bar" and then reading the attribute "baz" to get the value.
Example of the code that I use to do this:
XPathDocument document = new XPathDocument(filename);
XPathNavigator navigator = document.CreateNavigator();
XPathNavigator node = navigator.SelectSingleNode("//foo/bar");
if(node.HasAttributes)
{
Console.WriteLine(node.GetAttribute("baz", string.Empty));
}
Now, if the call to navigator.SelectSingleNode fails, it will return a NullReferenceException or an XPathException. I can catch both of those and refactor the above into a test to see whether or not a given XPath returns an exception, but I was wondering whether there was a better way?
I didn't see anything obvious in the Intellisense. XPathNavigator has .HasAttributes and .HasChildren but short of iterating through the path one node at a time, I don't see anything nicer to use.
If you've given valid XPath but it doesn't match anything, SelectSingleNode won't throw a NullReferenceException - it will just return null.
If you pass SelectSingleNode some syntactically invalid XPath, that's when it will throw an XPathException.
So normally, you'd just need to test whether the returned value was null or not.
var baz = navigator.SelectSingleNode("//foo/bar/#baz");
if (baz != null) Console.WriteLine(baz);
I think it is not good to create an XMLNode object by executing navigator.SelectSingleNode(...).
You have to use navigator.Evaluate() instead:
if (Convert.ToBoolean(navigator.Evaluate(#"boolean(//foo/bar)"))) {...}
From memory, may contain errors.
XDocument doc = XDocument.Load("foo.xml");
var att = from a in doc.Descendants("bar")
select a.Attribute("baz")
foreach (var item in att) {
if (item != null) { ... }
}
If node == null then node.HasAttributes will throw a NullReferenceException. This situation will occur when //foo/bar does not match any elements in the XML document.
var node = XDocument.Load(filename)
.Descendants("bar")
.SingleOrDefault(e=>e.Attribute("baz") != null);
if (node != null) Console.WriteLine(node.Attribute("baz").Value);
I would probably be more specific in my xpath.
var doc = XDocument.Load(fileName);
var results = from r in doc.XPathSelectElements("/foo/bar[count(#baz) > 0]")
select r.Attribute("baz");
foreach (String s in results)
Console.WriteLine(s);

Categories