2010-02-22 17 views
13

F # newbieanalisi con F # LINQ to XML

ho 2 file XML in 2 cartelle c:\root\a\file.xml e c:\root\b\file.xml

Hanno struttura identica:

<parent> 
    <property name="firstName">Jane</property> 
    <property name="lastName">...</property> 
    <property name="dateOfBirth">...</property>> 
</parent> 

ho bisogno di scegliere il file quale nodo proprietà con nome firstName ha il valore Jane.

In F # (possibilmente utilizzando System.Xml.Linq) Ho provato diverse soluzioni ma nessuna funzionava finora. Qualcuno disposto ad aiutare?

risposta

36

Sarebbe utile se si potesse mostrare un po 'di codice che si è tentato - qualcuno può poi spiegare qual è il problema, in modo da poter imparare di più rispetto a quando il codice qualcuno solo i messaggi che funziona . In ogni caso, sarà necessario fare riferimento ad alcuni assembly con lo System.Xml.Linq e aprire prima lo spazio dei nomi. In F # interattivo, è possibile scrivere in questo modo (in F # di progetto, basta usare Aggiungi riferimento finestra):

#r "System.Core.dll" 
#r "System.Xml.Linq.dll" 
open System.Xml.Linq 

Quando si utilizza XLinq in F #, è necessario un semplice funzione di utilità per la conversione di stringhe in XName oggetto (che rappresenta un nome di elemento/attributo). C'è una conversione implicita in C#, ma purtroppo non funziona in F #.

let xn s = XName.Get(s) 

Poi si può caricare il documento XML utilizzando la classe XDocument e utilizzare Element metodo per ottenere un unico elemento "genitore". Poi si può chiamare Elements per ottenere tutti gli elementi annidati "proprietà":

let xd = XDocument.Load("file.xml") 
let props = xd.Element(xn "parent").Elements(xn "property") 

Ora è possibile cercare gli elementi per trovare l'unico elemento con il valore attributo specificato. Ad esempio, utilizzando Seq.tryFind (che permette anche di gestire il caso in cui l'elemento non viene trovato):

let nameOpt = props |> Seq.tryFind (fun xe -> 
    xe.Attribute(xn "name").Value = "firstName") 

Ora, il valore nameOpt è di tipo option<XElement> in modo da poter pattern matching su di esso per vedere se l'elemento è stato trovato (ad esempio Some(el)) o se non è stato trovato (None).

EDIT: Un altro modo di scrivere questo è quello di utilizzare le espressioni di sequenza e poi basta prendere il primo elemento (questo non gestisce il caso quando l'elemento non viene trovato):

let nameEl = 
    seq { for el in xd.Element(xn "parent").Elements(xn "property") do 
      if xe.Attribute(xn "name").Value = "firstName" then yield xe } 
    |> Seq.head 
+1

Ora mi sento stupido! Ho pensato che il punto di utilizzo di f # '' query {} '' espressioni e linq erano, che potevi astrarre dall'origine dati.Quindi - impossibile come lo ritengo - non c'è modo di fare uno standard '' query {for foo in xmlBar do ...} '' tipo di cosa? – BitTickler

+0

Nota inoltre è necessario fare riferimento a System.Xml se non è già referenziato – byteit101

12

Non è necessario utilizzare LINQ per questo. Ecco un modo per farlo:

open System.Xml.XPath 

let getName (filename:string) = 
    let xpath = XPathDocument(filename).CreateNavigator() 
    let node = xpath.SelectSingleNode(@"parent/property[@name='firstName']") 
    node.Value 

let files = [@"c:\root\a\file.xml"; @"c:\root\b\file.xml"] 
let fileForJane = files |> List.find (fun file -> getName file = "Jane") 
6

Inoltre, è può mescolare la soluzione di KVB con operatore (?):

let (?) (fname: string) (nodeName: string) : string = 
    let xpath = XPathDocument(fname).CreateNavigator() 
    let node = xpath.SelectSingleNode(@"parent/property[@name='"^nodeName^"']") 
    node.Value;; 

val (?) : string -> string -> string 

> "i:/a.xml"?firstName;; 
val it : string = "Jane" 
+0

Non capisco. Non riesco a vedere quale operatore nel simbolo e nell'operatore facciano riferimento a questo. Potete fornire un riferimento? – dudeNumber4

2

mi piace questo approccio migliore

#r "System.Core.dll" 
#r "System.Xml.Linq.dll" 
open System.Xml.Linq 

let xn s = XName.Get(s) 

let xd = XDocument.Load("file.xml") 
let fnp = xd.Descendants(xn "property") 
     .Where(fun (p : XElement) -> p.Attribute(xn "name").Value = "firstName") 
     .Single() 
+0

nice one @ A-Dubb – scarpacci

+0

Grazie fratello. Apprezzalo.. –