2010-02-22 5 views
13

F # débutantfichier XML Parsing avec F # LINQ to XML

je 2 fichiers XML dans 2 dossiers c:\root\a\file.xml et c:\root\b\file.xml

Ils ont une structure identique:

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

je dois choisir le fichier le nœud de propriété avec le nom firstName a la valeur Jane. En F # (éventuellement en utilisant System.Xml.Linq) J'ai essayé plusieurs solutions mais aucune n'a fonctionné jusqu'à présent. Quelqu'un est-il prêt à aider?

Répondre

36

Il serait utile si vous pouviez montrer un code que vous avez essayé - quelqu'un peut alors expliquer quel est le problème, vous pouvez en apprendre plus que lorsque le code quelqu'un seulement des messages qui fonctionne . Quoi qu'il en soit, vous devrez référencer certains assemblages avec le System.Xml.Linq et ouvrir d'abord l'espace de noms. En F # interactive, vous pouvez l'écrire comme celui-ci (dans le projet F #, il suffit d'utiliser Ajouter Référence dialogue):

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

Lorsque vous utilisez XLinq en F #, vous avez besoin d'une simple fonction utilitaire pour convertir des chaînes en XName objet (qui représente un nom d'élément/attribut). Il y a une conversion implicite en C#, mais cela ne fonctionne malheureusement pas en F #.

let xn s = XName.Get(s) 

Ensuite, vous pouvez charger votre document XML en utilisant la classe XDocument et utiliser la méthode Element pour obtenir un seul élément « parent ». Ensuite, vous pouvez appeler Elements pour obtenir tous les éléments de « propriété » imbriqués:

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

Maintenant, vous pouvez rechercher les éléments pour trouver l'élément un avec la valeur d'attribut spécifié. Par exemple en utilisant Seq.tryFind (qui vous permet également de gérer le cas où l'élément ne se trouve pas):

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

Maintenant, la valeur nameOpt est de type option<XElement> afin que vous puissiez correspondance de motif sur elle pour voir si l'élément était trouvé (par exemple Some(el)) ou s'il n'a pas été trouvé (None).

EDIT: Une autre façon d'écrire est d'utiliser des expressions de séquence, puis il suffit de prendre le premier élément (ce qui ne gère pas le cas lorsque l'élément ne se trouve pas):

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

Maintenant, je me sens stupide! Je pensais que l'utilisation des expressions f # '' query {} '' et linq étaient des points que vous pouviez extraire de la source de données.Donc - impossible comme je le pense - n'y a-t-il aucun moyen de faire une sorte standard de '' query {for foo in xmlBar do ...} ''? – BitTickler

+0

Notez que vous devez également référencer System.Xml s'il n'est pas déjà référencé. – byteit101

12

Vous n'avez pas besoin d'utiliser LINQ pour cela. Voici une façon de le faire:

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

En outre, vous peut mélanger la solution de KVB avec opérateur (?):

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

Ne comprends pas. Je ne peux pas voir quel opérateur dans le symbole et la référence de l'opérateur c'est. Pouvez-vous fournir une référence? – dudeNumber4

2

J'aime cette approche mieux

#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

Merci à tous. Appréciez-le .. –