Разбор XML-файла с F # linq в XML - PullRequest
       13

Разбор XML-файла с F # linq в XML

13 голосов
/ 23 февраля 2010

F # новичок

У меня есть 2 файла XML в 2 папках c:\root\a\file.xml и c:\root\b\file.xml

Они имеют одинаковую структуру:

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

Мне нужно выбрать файл, у которого узел свойства с именем firstName имеет значение Jane.

В F # (возможно, с использованием System.Xml.Linq) Я пробовал несколько решений, но ни одно из них не работает. Кто-нибудь готов помочь?

Ответы [ 4 ]

36 голосов
/ 23 февраля 2010

Было бы полезно, если бы вы могли показать какой-то код, который вы пробовали - кто-то может объяснить, в чем проблема, чтобы вы могли узнать больше, чем когда кто-то просто публикует код, который работает. В любом случае вам нужно будет сослаться на некоторые сборки с помощью System.Xml.Linq и сначала открыть пространство имен. В F # интерактиве вы можете написать это так (в проекте F # просто используйте диалог Добавить ссылку ):

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

При использовании XLinq в F # вам нужна простая служебная функция для преобразования строк в XName объект (который представляет имя элемента / атрибута). В C # есть неявное преобразование, но, к сожалению, это не работает в F #.

let xn s = XName.Get(s)

Затем вы можете загрузить свой XML-документ, используя класс XDocument, и использовать метод Element, чтобы получить один «родительский» элемент. Затем вы можете вызвать Elements, чтобы получить все вложенные элементы "свойства":

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

Теперь вы можете искать элементы, чтобы найти один элемент с указанным значением атрибута. Например, используя Seq.tryFind (что также позволяет обрабатывать случай, когда элемент не найден):

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

Теперь значение nameOpt имеет тип option<XElement>, поэтому вы можете сопоставить с ним шаблон, чтобы увидеть, был ли найден элемент (например, Some(el)) или он не был найден (None).

EDIT: Другой способ написать это - использовать выражения последовательности, а затем просто взять первый элемент (это не обрабатывает случай, когда элемент не найден):

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
12 голосов
/ 23 февраля 2010

Вам не нужно использовать LINQ для этого. Вот один из способов сделать это:

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 голосов
/ 23 февраля 2010

Также вы можете смешать решение kvb с оператором (?):

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"
2 голосов
/ 03 октября 2010

Мне нравится такой подход лучше

#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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...