2012-09-30 3 views
9

Così ho questo codice:Come eseguire il cast di XPathEvalute quando può essere XElement o XAttribute?

List<PriceDetail> prices = 
       (from item in xmlDoc.Descendants(shop.DescendantXName) 
       select new PriceDetail 
       { 
        Price = GetPrice(item.Element(shop.PriceXPath).Value), 
        GameVersion = GetGameVersion(((IEnumerable)item.XPathEvaluate(shop.TitleXPath)).Cast<XAttribute>().First<XAttribute>().Value, item.Element(shop.PlatformXPath).Value), 
        Shop = shop, 
        Link = item.Element(shop.LinkXPath).Value, 
        InStock = InStock(item.Element(shop.InStockXPath).Value) 
       }).ToList<PriceDetail>(); 

Il problema che ho è questo codice:

((IEnumerable)item.XPathEvaluate(shop.TitleXPath)).Cast<XAttribute>().First<XAttribute>().Value 

A volte l'oggetto da XPathEvaluate potrebbe essere XElement e poi la fusione non funziona. Quindi quello di cui ho bisogno è un cast che funzioni sia con XAttribute sia con XElement.

Qualche suggerimento?

+0

_Sometimes la voce potrebbe essere XElement e poi .._ - il 'item' in questo codice può _solo_ essere XElement. Rende la domanda difficile da seguire. –

+0

Mi spiace confondervi. L'oggetto da XPathEvaluate potrebbe essere da XElement o da XAttribute dipendente da xpath. – Spindel

risposta

12

Modifica la tua espressione XPath (shop.TitleXPath) da:

someXPathExpression 

a:

string(someXPathExpression) 

Quindi è possibile semplificare il codice a poco:

string result = item.XPathEvaluate(shop.TitleXPath) as string; 

Complete esempio funzionante:

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


    } 
} 

Quando viene eseguito questo programma, la stessa espressione XPath è applicato su due diversi Documenti XML e, indipendentemente dal fatto che l'argomento a string() è un attributo la prima volta ed è un elemento sul secondo, otteniamo i risultati corretti - w RITTEN alla console:

attribute value 
element value 
+0

Non ha funzionato: espressione non valida 'stringa'. – Spindel

+0

@Spindel, vedere la risposta aggiornata - ora contiene una soluzione completa di lavoro. –

+0

Ora capisco. Grazie! – Spindel

1

Prima di effettuare il cast è possibile controllare il tipo utilizzando un codice come questo:

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

Ok, ma non capisco davvero come posso adattarlo al mio codice. Potresti fare un esempio con il mio codice sopra? – Spindel

6

XElement e XAttribute sono entrambe le forme di XObject, quindi, se l'istanza generica di tipo XObject sarà sufficiente per le vostre esigenze, modificare il cast <XAttribute> per lanciare <XObject>.

Se questo non funziona per la vostra situazione specifica, si fanno uso di OfType <XAttribute> o OfType <XElement> per filtrare per uno o l'altro, ma ciò richiederebbe due passaggi sopra l'ingresso, uno per filtrare per XElement e un secondo passare al filtro per XAttribute.

3

La soluzione di Dimitre restituisce una stringa vuota se l'elemento non viene trovato; non possiamo distinguerlo dal reale valore vuoto. Così ho dovuto fare questo metodo di estensione che gestisce più i risultati di query XPath e restituisce l'enumerazione vuota se non viene trovato nulla:

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; 
    } 
} 
Problemi correlati