使用F#linq将XML文件解析为XML

Ene*_*nes 13 xml f# linq-to-xml

F#新手

我有2个文件夹2个XML文件c:\root\a\file.xmlc:\root\b\file.xml

它们具有相同的结构:

<parent>
   <property name="firstName">Jane</property>
   <property name="lastName">...</property>
   <property name="dateOfBirth">...</property>>
</parent>
Run Code Online (Sandbox Code Playgroud)

我需要选择具有名称的属性节点具有firstName值Jane的文件.

在F#中(可能通过使用System.Xml.Linq)我尝试了几种解决方案但到目前为止都没有.有人愿意帮忙吗?

Tom*_*cek 37

如果你能展示一些你尝试过的代码会很有用 - 有人可以解释问题是什么,所以你可以学到更多东西,而不是有人发布有用的代码.无论如何,您需要首先引用一些程序集System.Xml.Linq并打开命名空间.在F#interactive中,你可以像这样编写它(在F#项目中,只需使用Add Reference对话框):

#r "System.Core.dll"
#r "System.Xml.Linq.dll"
open System.Xml.Linq
Run Code Online (Sandbox Code Playgroud)

在F#中使用XLinq时,需要一个简单的实用程序函数将字符串转换为XName对象(表示元素/属性名称).在C#中存在隐式转换,但遗憾的是在F#中不起作用.

let xn s = XName.Get(s)
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用XDocumentclass和use Element方法加载XML文档以获取单个"父"元素.然后你可以调用Elements以获取所有嵌套的"property"元素:

let xd = XDocument.Load("file.xml")
let props = xd.Element(xn "parent").Elements(xn "property")
Run Code Online (Sandbox Code Playgroud)

现在,您可以搜索元素以查找具有指定属性值的一个元素.例如using Seq.tryFind(也允许你在找不到元素时处理这种情况):

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

现在,值nameOpt是类型,option<XElement>因此您可以在其上进行模式匹配,以查看是否找到了元素(例如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
Run Code Online (Sandbox Code Playgroud)


kvb*_*kvb 12

您不需要为此使用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")
Run Code Online (Sandbox Code Playgroud)


ssp*_*ssp 6

此外,您可以将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"
Run Code Online (Sandbox Code Playgroud)