使用 F# 数据按顺序遍历 multiple/different xml 个元素

Looping through multiple/different xml elements in their order with F# data


是否可以使用F#按顺序循环遍历以下文档,伪代码真的很伪:)

<root>
   <element_a>1</element_a>
   <element_b>2</element_b>
   <element_c>3</element_c>
   <element_a>4</element_a>
</root>

伪代码:

root.? |> Array.iter ( fun e ->
   printfn "%s:%s" e.DisplayName e.Value
)

结果:

element_a:1
element_b:2
element_c:3
element_a:4

所以,

  1. 我可以按原始顺序遍历不同的元素吗?
  2. 如果是,我怎么知道它是哪个元素?

XML 类型提供程序不提供任何类型的方法来执行此操作。也就是说,作为一些提供的类型的集合,可以按顺序访问所有 children 将是一个有用的扩展!

但是,您可以使用底层 XElement 表示来遍历所有 children,然后将它们返回到 XML 生成的 statically-typed 表示类型提供者。

使用你的基本示例,所有元素的类型都被推断为 int,所以这并不是那么有趣(没有 children...),但假设你有像这样不同的元素都有相同的 child 元素 a 和属性 n:

type Demo = XmlProvider<"""<root>
   <element_a><a n="foo" /></element_a>
   <element_b><a n="bar" /></element_b>
   <element_c><a n="zoo" /></element_c>
   <element_a><a n="goo" /></element_a>
</root>""">

您现在可以使用 root.XElement.Descendants():

以无类型的方式获取元素
let root = Demo.GetSample()
for child in root.XElement.Descendants() do
  let asA = new Demo.ElementA(child)
  printfn "%A" asA.A.N

一旦我们有了 child 元素,我们就 re-wrap 将它变成静态类型的表示。在这里,我们总是使用为 <element_a> 生成的类型(这可能是错误的 - 如果其他元素具有不同的结构,但它在这里工作得很好)。

您可以创建一个 XDocument 并遍历根的子节点:

#r "System.XML.Linq"
open System.IO;
open System.Xml.Linq;

let xml = @"
            <root>
               <element_a>1</element_a>
               <element_b>2</element_b>
               <element_c>3</element_c>
               <element_a>4</element_a>
            </root>"
let xdoc = XDocument.Load(new StringReader(xml))
let rootEl = xdoc.Elements() |> Seq.head
rootEl.Elements() |> Seq.iter (fun el -> printfn "%s: %s" el.Name.LocalName el.Value)

应该产生所需的输出。

如果要区分同名元素,可以将 Seq.iterSeq.iteri 交换,这会将序号索引传递给提供的函数:

rootEl.Elements() |> Seq.iteri (fun idx el -> printfn "Idx %d: %s: %s" idx el.Name.LocalName el.Value)

产生:

Idx 0: element_a: 1
Idx 1: element_b: 2
Idx 2: element_c: 3
Idx 3: element_a: 4