So I have this code:
List prices =
(from item in xmlDoc.Descendants(shop.DescendantXName)
select new PriceDet
Change your XPath expression (shop.TitleXPath
) from:
someXPathExpression
to:
string(someXPathExpression)
Then you can simplify the code to just:
string result = item.XPathEvaluate(shop.TitleXPath) as string;
Complete working example:
using System;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
class TestXPath
{
static void Main(string[] args)
{
string xml1 =
@"<t>
<a b='attribute value'/>
<c>
<b>element value</b>
</c>
<e b='attribute value'/>
</t>";
string xml2 =
@"<t>
<c>
<b>element value</b>
</c>
<e b='attribute value'/>
</t>";
TextReader sr = new StringReader(xml1);
XDocument xdoc = XDocument.Load(sr, LoadOptions.None);
string result1 = xdoc.XPathEvaluate("string(/*/*/@b | /*/*/b)") as string;
TextReader sr2 = new StringReader(xml2);
XDocument xdoc2 = XDocument.Load(sr2, LoadOptions.None);
string result2 = xdoc2.XPathEvaluate("string(/*/*/@b | /*/*/b)") as string;
Console.WriteLine(result1);
Console.WriteLine(result2);
}
}
When this program is executed, the same XPath expression is applied on two different XML documents and, regardless of the fact that the argument to string()
is an attribute the first time and is an element on the second, we get the correct results -- written to the Console:
attribute value
element value
Before you make the cast you can check for the type using a code like this:
XElement e = item as XElement;
XAttribute a = item as XAttribute;
if(e != null)
//item is of type XElement
else
//item is of type XAttribute
XElement
and XAttribute
are both forms of XObject
, so if a generic instance of type XObject
will suffice for your needs, change your Cast<XAttribute>
to Cast<XObject>
.
If that won't work for your specific situation, you make use of OfType<XAttribute>
or OfType<XElement>
to filter for one or the other, but that would require two passes over the input, one to filter for XElement
and a second pass to filter for XAttribute
.
Dimitre's solution returns empty string if the element is not found; we can't distinguish it from actual empty value. So I had to make this extension method that handles multiple results by XPath query and returns empty enumeration if nothing is found:
public static IEnumerable<string> GetXPathValues(this XNode node, string xpath)
{
foreach (XObject xObject in (IEnumerable)node.XPathEvaluate(xpath))
{
if (xObject is XElement)
yield return ((XElement)xObject).Value;
else if (xObject is XAttribute)
yield return ((XAttribute)xObject).Value;
}
}