2010-02-22 25 views
13

F # novatode análisis con F # LINQ to XML

tengo 2 archivos XML en 2 carpetas c:\root\a\file.xml y c:\root\b\file.xml

Tienen una estructura idéntica:

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

tengo que elegir el archivo cuyo nodo de propiedad con el nombre firstName tiene el valor Jane.

En F # (posiblemente usando System.Xml.Linq) He intentado varias soluciones, pero ninguna funciona hasta ahora. ¿Alguien dispuesto a ayudar?

Respuesta

36

Sería útil si se pudiera mostrar algo de código que ha intentado - alguien, entonces se puede explicar lo que es el problema, por lo que se puede aprender más que cuando el código de alguien que acaba de mensajes que funciona . De todos modos, deberá hacer referencia a algunos ensambles con el System.Xml.Linq y abrir primero el espacio de nombres. En F # interactiva, se puede escribir como esto (en el proyecto F #, sólo tiene que utilizar Agregar referencia de diálogo):

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

Al utilizar XLinq en Fa #, se necesita una función sencilla utilidad para convertir cadenas a XName objeto (que representa un nombre de elemento/atributo). Hay una conversión implícita en C#, pero lamentablemente no funciona en F #.

let xn s = XName.Get(s) 

A continuación, puede cargar el documento XML utilizando la clase XDocument y utilizar Element método para obtener un solo elemento "padre". A continuación, puede llamar Elements para obtener todos los elementos anidados "propiedad":

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

Ahora puede buscar los elementos para encontrar el elemento con el valor de atributo especificado. Por ejemplo, usando Seq.tryFind (que también le permite manejar el caso cuando no se encuentra el elemento):

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

Ahora, el valor nameOpt es de tipo option<XElement> para que pueda coincidir patrón en él para ver si el elemento fue encontrado (por ejemplo, Some(el)) o si no se encontró (None).

EDIT: Otra forma de escribir esto es el uso de expresiones de secuencia y luego tomar el primer elemento (esto no maneja el caso cuando no se encuentra elemento):

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

¡Ahora me siento estúpido! Pensé que el punto de usar las expresiones f # '' query {} '' y linq, era que se podía abstraer de la fuente de datos.Así que, por imposible que sea, ¿no hay forma de hacer un '' query {for foo in xmlBar do ...} '' tipo de cosa? – BitTickler

+0

Tenga en cuenta que también necesita hacer referencia a System.Xml si no está ya referenciado – byteit101

12

No necesita usar LINQ para esto. He aquí una manera de hacerlo:

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

Además, puede mezclar la solución de kvb con el operador (?):

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

No lo entiendo. No puedo ver qué operador en el símbolo y la referencia del operador es esto. ¿Puedes proporcionar una referencia? – dudeNumber4

2

me gusta este enfoque mejor

#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

Gracias hermano. Lo aprecio.. –