tags:

views:

5015

answers:

5

Hello,

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;
}
A: 

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);
    }
}
Andrew Hare
LINQ to XML is not the "new XML API". There certainly reasons for using the existing System.Xml namespace. LINQ is simply another means of using the classes in that namespace.
Adam Robinson
I disagree - System.Xml.Linq contains an array of new types for dealing with XML. It truly is a new API for working with XML.
Andrew Hare
A: 

Maybe this will work for you:

string s = elem.SelectSingleNode("somepath") != null ? elem.SelectSingleNode("somepath").value : ""
tessa
That will work, but SelectSingleNode will be executed twice, incurring an unnecessary performance hit.
Thorarin
A: 

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.

Paige Watson
This won't work: if SelectSingleNode returns null, there is nothing to call InnerText on.
Thorarin
@Thorarn: Correct. The question (and example) show that the node does exist, just without text between the tags. I would highly recommend that you check for a Null node before checking for a value.
Paige Watson
You're right about the node existing... His explanation mentioned something about SelectSingleNode returning null, but that can't be the case if there's an element there (empty or not)
Thorarin
A: 

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).

Thorarin
This would work nicely, as it also check to make sure the node itself is != Null, however the node can exist and still return node.Value as NULL, based on what the NodeType is.
Paige Watson
@Paige: that's certainly possible. If you can't make that assumption about your XML, you should do more checking. Or validate against an XSD beforehand. I've updated the method slightly to not return null now.
Thorarin
+1  A: 

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;
Pavel Minaev
That was the problem, thanks
Roee Adler