使用 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
所以,
- 我可以按原始顺序遍历不同的元素吗?
- 如果是,我怎么知道它是哪个元素?
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.iter
与 Seq.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
是否可以使用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
所以,
- 我可以按原始顺序遍历不同的元素吗?
- 如果是,我怎么知道它是哪个元素?
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.iter
与 Seq.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