Новичок 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
) Я попробовал несколько решений, но ни один работающий до сих пор. Кто-либо готовый помочь?
Было бы полезно, если бы вы могли показать какой-нибудь код, который вы пробовали, - затем кто-нибудь может объяснить, в чем проблема, чтобы вы могли узнать больше, чем когда кто-то просто размещает код, который работает. В любом случае вам нужно будет сослаться на некоторые сборки с помощью 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
, чтобы вы могли сопоставить его с образцом, чтобы увидеть, был ли элемент найден (например, Some (el)
) или не был найден ( None
).
РЕДАКТИРОВАТЬ: Другой способ записать это - использовать выражения последовательности, а затем просто взять первый элемент (это не относится к случаю, когда элемент не найден):
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
Для этого не нужно использовать 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")
Также, вы можете смешать решение 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"