How to cast XPathEvalute when it can be XElement or XAttribute?

前端 未结 4 1892
自闭症患者
自闭症患者 2021-02-20 06:29

So I have this code:

List prices =
                (from item in xmlDoc.Descendants(shop.DescendantXName)
                 select new PriceDet         


        
相关标签:
4条回答
  • 2021-02-20 06:40

    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
    
    0 讨论(0)
  • 2021-02-20 06:43

    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
    
    0 讨论(0)
  • 2021-02-20 06:43

    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.

    0 讨论(0)
  • 2021-02-20 06:50

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题