C# check xml node the safe way? - c#

I have the code:
if (Element.SelectSingleNode("/rsp/merged_poco/organizations/organization/name") != null)
this.Organization = Element.SelectSingleNode("/rsp/merged_poco/organizations/organization/name").InnerText;
However there is a changes that "merged_poco" may not have "organizations". Would my code result in an error? Or just that null check suffice?
In my null check should I also check to see if "organizations" and "organization" exist?

Your code should suffice. You can also optimize it as the following:
XmlNode node = Element.SelectSingleNode("/rsp/merged_poco/organizations/organization/name");
if (node != null)
this.Organization = node.InnerText;

If you want to know how exactly does a certain method behave, you should read its documentation:
Return Value
Type: System.Xml.XmlNode
The first XmlNode that matches the XPath query or null if no matching node is found.

Related

Add null as value when element does not exist in XML file C#

In C# I'm using the following to get some elements from an XML file:
var TestCaseDescriptions = doc.SelectNodes("//testcase/htmlcomment");
This works fine and gets the correct information but when my testcase has no htmlcomment it won't add any entry in the XmlNodeList TestCaseDescriptions.
When there's not htmlcomment I would like to have the value "null" as string the TestCaseDescriptions. So in the end I would have an XMLNodeList like
htmlcomment
htmlcomment
null
htmlcomment
htmlcomment
Can anyone describe or create a sample how to make this happen?
var TestCaseDescriptions = doc.SelectNodes("//testcase/htmlcomment");
When there's not htmlcomment I would like to have the value "null" as string the TestCaseDescriptions.
Your problem comes from the fact that if there is no htmlcomment, the number of selected nodes will be one less. The current answer shows what to do when the htmlcomment element is present, but empty, but I think you need this instead, if indeed the whole htmlcomment element is empty:
var testCases = doc.SelectNodes("//testcase");
foreach (XmlElement element in testCases)
{
var description = element.SelectSingleNode("child::htmlcomment");
string results = description == null ? "null" : description.Value;
}​
In above code, you go over each test case, and select the child node htmlcomment of the test case. If not found, SelectSingleNode returns null, so the last line checks for the result and returns "null" in that case, or the node's value otherwise.
To change this result into a node, you will have to create the node as a child to the current node. You said you want an XmlNodeList, so perhaps this works for you:
var testCaseDescriptions = doc.SelectNodes("//testcase");
foreach (XmlElement element in testCaseDescriptions)
{
var comment = element.SelectSingleNode("child::htmlcomment");
if (comment == null)
{
element.AppendChild(
doc.CreateElement("htmlcomment")
.AppendChild(doc.CreateTextNode("none")));
}
}​
After this, the node set is updated.
Note: apparently, the OP mentions that element.SelectSingleNode("child::htmlcomment"); does not work, but element.SelectSingleNode("./htmlcomment"); does, even though technically, these are equal expressions from the point of XPath, and should work according to Microsoft's documentation.
Try this
XmlDocument doc = new XmlDocument();
var TestCaseDescriptions = doc.SelectNodes("//testcase/htmlcomment");
foreach (XmlElement element in TestCaseDescriptions)
{
string results = element.Value == null ? "" : element.Value;
}​

xPath to get sibling value for element with specific value

Given this XML:
<InitResponse>
<LottoToken>908ec70b308adf10d04db1478ef9b01b</LottoToken>
<GameInfoList>
<GameInfo>
<Draw>
<gameId>L649</gameId>
<draw>3035</draw>
</Draw>
</GameInfo>
<GameInfo>
<Draw>
<gameId>BC49</gameId>
<draw>2199</draw>
</Draw>
</GameInfo>
</GameInfoList>
</InitResponse>
I need to get the draw number based on a specific gameId. For example if I specify gameID L649 I need to get 3035.
The following works in several online evaluators, but not in C#. It says it cannot find it. Suggestions?
/InitResponse/GameInfoList/GameInfo/Draw/draw[preceding-sibling::gameId='L649']
C# Code I've tried:
XmlNode node = xmlDoc.SelectSingleNode("/InitResponse/GameInfoList/GameInfo/Draw/draw[preceding-sibling::gameId='L649']");
... where xmlDoc is an xmlDocument object loaded with the xml. the node variable ends up with a null value which seems to indicate there was no match found.
Here is xpath (with Linq)
var xdoc = XDocument.Load(path_to_xml);
string xpath = "/InitResponse/GameInfoList/GameInfo/Draw[gameId='L649']/draw";
var draw = xdoc.XPathSelectElement(xpath);
if (draw != null) // check if draw with gameId found in xml
value = (int)draw;
Also you can use pure Linq to Xml (but in this case xpath looks more compact):
var draw = xdoc.Descendants("GameInfo")
.SelectMany(g => g.Elements("Draw"))
.SingleOrDefault(d => (string)d.Element("gameId") == "L649");
if (draw != null)
value = (int)draw.Element("draw");
Using XmlDocument
I didn't saw something wrong in your XPath statement, look on the following:
(So i guess there is something else that is wrong)
XmlDocument myDoc = new XmlDocument();
String str = #"<InitResponse>
<LottoToken>908ec70b308adf10d04db1478ef9b01b</LottoToken>
<GameInfoList>
<GameInfo>
<Draw>
<gameId>L649</gameId>
<draw>3035</draw>
/Draw>
</GameInfo>
<GameInfo>
<Draw>
<gameId>BC49</gameId>
<draw>2199</draw>
</Draw>
</GameInfo>
</GameInfoList>
</InitResponse>";
myDoc.LoadXml(str);
XmlNode node =
myDoc.SelectSingleNode("/InitResponse/GameInfoList/GameInfo/Draw/draw[preceding-sibling::gameId='L649']");
The node which returns from the result is: 3035
Note: your first note have to be <InitResponse> otherwise it will returns null

How to get parameter values from an XmlNode in C#

How do I get the values for parameters in a XmlNode tag. For example:
<weather time-layout="k-p24h-n7-1">
<name>Weather Type, Coverage, and Intensity</name>
<weather-conditions weather-summary="Mostly Sunny"/>
</weather>
I want to get the value for the parameter 'weather-summary' in the node 'weather-conditions'.
var node = xmldoc.SelectSingleNode("weather/weather-conditions");
var attr = node.Attributes["weather-summary"];
In the interest of completeness, the .Net 3.5 way should be given as well:
Assuming
XDocument doc = XDocument.Parse(#"<weather time-layout='k-p24h-n7-1'>
<name>Weather Type, Coverage, and Intensity</name>
<weather-conditions weather-summary='Mostly Sunny'/></weather>");
Then either
return doc.Element("weather").Element("weather-conditions").Attribute("weather-summary").Value;
Or
return doc.Descendants("weather-conditions").First().Attribute("weather-summary").Value;
Will give you the same answer.

C# XmlElement: SelectSingleNode returns null for empty string?

I'm new to C#, and just started using XmlElement and its SelectSingleNode method. In my XML file there's a tag that may have a value (i.e. <tag>value</tag>) or be empty (i.e. <tag></tag>). If it's empty, SelectSingleNode returns null.
I'm currently using the following code to catch the value of the tag:
XmlElement elem = ....
string s = elem.SelectSingleNode("somepath").Value;
This code obviously raises an exception for empty tags. However, for me an empty tag is a valid value, where I expect the value of my string to be "".
Wrapping each call to SelectSingleNode with try...catch seems a huge waste of code (I have many fields that may be empty), and I'm sure there's a better way to achieve this.
What is the recommended approach?
EDIT:
Following requests, a sample XML code will be:
<Elements>
<Element>
<Name>Value</Name>
<Type>Value</Type> <-- may be empty
<Color>Value</Color>
</Element>
<Element>
<Name>Value</Name>
<Type>Value</Type>
<Color>Value</Color>
</Element>
</Elements>
The CS code:
XmlDocument doc = new XmlDocument();
doc.Load("name.xml");
foreach (XmlElement elem in doc.SelectNodes("Elements/Element"))
{
myvalue = elem.SelectSingleNode("Type/text()").Value;
}
Your sample code:
myvalue = elem.SelectSingleNode("Type/text()").Value;
is where the problem is. The XPath expression you've used there doesn't mean "give me text of element Type". It means "give me all child text nodes of element Type". And an empty element doesn't have any child text nodes (a text node cannot be empty in XPath document model). If you want to get text value of the node, you should use:
myvalue = elem.SelectSingleNode("Type").InnerText;
The recommended approach would be to use .NET's new XML API (namely LINQ to XML).
Here is an example:
using System;
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main()
{
String xml = #"<Root><Value></Value></Root>";
var elements = XDocument.Parse(xml)
.Descendants("Value")
.Select(e => e.Value);
}
}
http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.value(VS.71).aspx
Because the "value" returned depends on the NodeType, there is a chance that the node will be interpreted as a type that can return NULL.
You might be better off using:
XmlElement elem = ....
string s = elem.SelectSingleNode("somepath").InnerText;
as XMLNode.InnerText (or XmlNode.InnerXML) will return a string, including an empty string.
Maybe this will work for you:
string s = elem.SelectSingleNode("somepath") != null ? elem.SelectSingleNode("somepath").value : ""
When I'm actually bothering with XML DOM, you could write a helper method along the lines of:
static string NodeValue(XmlNode node, string defaultValue)
{
if (node != null)
return node.Value ?? defaultValue;
return defaultValue;
}
Then you can do the following if you're not sure your node will exist:
string s = NodeValue(elem.SelectSingleNode("Type"), String.Empty);
If keeps your code readable, especially if you're doing this for multiple elements.
All that being said, SelectSingleNode(..) does not return a null value if the tag is empty. The Value attribute will be null however. If you're just trying to work around that, this should do:
string s = elem.SelectSingleNode("Type").Value ?? String.Empty;
Edit: ah, you're using /text() to select the actual text node. You could just get rid of that part of the XPath, but the NodeValue method I supplied should still work (the "?? defaultValue" part is not needed in that case though).

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