【问题标题】:Parsing XML file with F# linq to XML使用 F# linq 将 XML 文件解析为 XML
【发布时间】:2015-04-10 05:31:57
【问题描述】:

F# 新手

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

它们具有相同的结构:

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

我需要选择名称为 firstName 的属性节点的值为 Jane 的文件。

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

【问题讨论】:

    标签: xml f# linq-to-xml


    【解决方案1】:

    如果您可以展示一些您尝试过的代码,这将非常有用 - 然后有人可以解释问题所在,这样您就可以比仅发布有效的代码学到更多。无论如何,您需要使用System.Xml.Linq 引用一些程序集并首先打开命名空间。在 F# 交互中,你可以这样写(在 F# 项目中,只需使用 Add Reference 对话框):

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

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

    let xn s = XName.Get(s)
    

    然后您可以使用XDocument 类加载您的XML 文档,并使用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&lt;XElement&gt;,因此您可以对其进行模式匹配以查看是否找到了该元素(例如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
    

    【讨论】:

    • 现在我觉得自己很愚蠢!我认为使用 f# query {}expressions 和 linq 的意义在于,您可以从数据源中抽象出来。所以 - 我认为不可能 - 有没有办法做一个标准的 query { for foo in xmlBar do ... } 之类的事情?
    • 请注意,如果尚未引用 System.Xml,您还需要引用它
    【解决方案2】:

    您不需要为此使用 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")
    

    【讨论】:

      【解决方案3】:

      此外,您可以将 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"
      

      【讨论】:

      • 不明白。我看不到符号和运算符引用中的哪个运算符。可以提供参考吗?
      【解决方案4】:

      我更喜欢这种方法

      #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()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-20
        • 1970-01-01
        • 2023-03-21
        • 2011-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多