XML 从 C# 扫描到 F#

XML scan from C# to F#

尝试学习 F#,并尝试在 F# 中重新实现以下函数

private string[] GetSynonyms(string synonyms)
{
    var items = Enumerable.Repeat(synonyms, 1)
                          .Where(s => s != null)
                          .Select(XDocument.Parse)
                          .Select(doc => doc.Root)
                          .Where(root => root != null)
                          .SelectMany(e => e.Elements(SynonymsNamespace + "synonym"))
                          .Select(e => e.Value)
                          .ToArray();

    return items;
}

我一个人走到这一步

let xname = XNamespace.Get "http://localuri"

let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"

let synonyms str =
    let items = [str]
    items
    |> List.map System.Xml.Linq.XDocument.Parse
    |> List.map (fun x -> x.Root)
    |> List.map (fun x -> x.Elements(xname + "synonym") |> Seq.cast<System.Xml.Linq.XElement>)
    |> Seq.collect (fun x -> x)
    |> Seq.map (fun x -> x.Value)

let a = synonyms syn

Dump a

现在我想知道是否有更实用的方法来编写相同的代码。

通过提取对独立函数的属性的访问,我得到了这个版本

let xname = XNamespace.Get "http://localuri"

let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"

let getRoot (doc:System.Xml.Linq.XDocument) = doc.Root

let getValue (element:System.Xml.Linq.XElement) = element.Value

let getElements (element:System.Xml.Linq.XElement) =
    element.Elements(xname + "synonym")
        |> Seq.cast<System.Xml.Linq.XElement>

let synonyms str =
    let items = [str]
    items
    |> List.map System.Xml.Linq.XDocument.Parse
    |> List.map getRoot
    |> List.map getElements
    |> Seq.collect (fun x -> x)
    |> Seq.map getValue

let a = synonyms syn

Dump a

但我还有一些顾虑

  1. 我可以用另一种方式重写 Seq.collect (fun x -> x) 吗?听起来多余
  2. 我可以在不创建新函数的情况下删除所有这些 (fun x -> x.Property) 吗?
  3. 如何实际 return 一个数组而不是一个 Seq<'a>(我理解是一个 IEnumerable<'a>)

谢谢

  1. Seq.collect (fun x -> x) 可以用预定义的id函数重写为 Seq.collect id

  2. 在 F# 4.0 中,只能为构造函数删除它。

  3. 使用Seq.toArray或Seq.toList

放弃 C# 代码并完全使用 F# 中的 XML-provider 会不会非常错误?在我的世界里,当存在其他解决方案时,总是 解析 XML 是错误的(除非我试图制造八角轮或其他在我之前做得更好的湿火药)。

在这方面,我什至会使用一些转换 (XSLT) 或选择 (XPATH/XQUERY),除非我可以使用 XML-provider 或一些 XSD (c#)生成代码。

如果由于某种原因 XML 如此非结构化以至于您实际上需要解析,那么 XML 可以说是错误的...

如果使用 XmlProvider,您可以免费获得命名空间、类型等...

#r @"..\correct\this\path\to\packages\FSharp.Data.2.2.5\lib\net40\FSharp.Data.dll"
#r "System.Xml.Linq"


open FSharp.Data
[<Literal>]
let syn = "<synonyms xmlns=\"http://localuri\"><synonym>a word</synonym><synonym>another word</synonym></synonyms>"

type Synonyms = XmlProvider<syn>

let a = Synonyms.GetSample()

a.Synonyms |> Seq.iter (printfn "%A")

请注意,XmlProvider 也可以将文件或 url 作为推断类型等的示例,您也可以将此代码作为示例,然后使用

let a = Synonyms.Load(stuff)

其中 stuff 是从流、文本阅读器或 URI 中读取并根据您的示例推断的。如果这是一些标准的数据放置,样本和内容甚至可能指向相同的 file/Uri。

另请参阅:http://fsharp.github.io/FSharp.Data/library/XmlProvider.html