如何递归获取 XmlProvider 的所有 XElement 子项

How can I recursively get all XElement children for an XmlProvider

我正在尝试使用 F# 为 C# 构建动态 type/class 构建器,来自以下 XML

<config target="string">
    <protocol>string</protocol>
    <about_path>string</about_path>
    <about_content>
        <name_path>string</name_path>
        <id_path>string</id_path>
        <version_path>string</version_path>
    </about_content>
</config>

使用下面的代码我可以很好地解析样本

module XmlParser =
    open FSharp.Data
    open System.Globalization
    open FSharp.Data.Runtime.BaseTypes
    open System.Xml.Linq

    [<Literal>]
    let targetSchema = "<config target=\"string\">
                            <protocol>string</protocol>
                            <about_path>string</about_path>
                            <about_content>
                                <name_path>string</name_path>
                                <id_path>string</id_path>
                                <version_path>string</version_path>
                            </about_content>
                        </config>"

    type Configuration = XmlProvider<targetSchema> 

现在的问题是我无法集中精力检索 about_content 标签的内部部分。

使用

解析实际的xml之后
let parsedValue = Configuration.Parse(xmlIn)

我试图了解 F# 中的递归处理,但被困在看起来像这样的非工作代码上(e 将是 parsedValue.XElement

let rec flatten ( e : System.Xml.Linq.XElement) (out:List<string>) = 
    if e.HasElements 
    then for inner in e.Elements -> flatten(inner)
    else e.Name.LocalName

我需要的是关于如何将 e.Name.LocalName 值收集到 sequence/List 作为递归结果的提示。我也可以忍受最后有一个 XElement 的列表。

函数flatten需要return一个序列,而不是一个单一的东西。

对于具有子元素的元素,您需要为每个元素调用 flatten,然后连接所有结果:

e.Elements() |> Seq.map flatten |> Seq.concat

(注意XElement.Elements是方法,不是属性,所以调用时需要加上())

对于单个元素,只需 return 它的名称包裹在单个元素序列中:

Seq.singleton e.Name.LocalName

综合起来:

let rec flatten (e : System.Xml.Linq.XElement) = 
    if e.HasElements 
    then e.Elements() |> Seq.map flatten |> Seq.concat
    else Seq.singleton e.Name.LocalName

(另请注意,我已经删除了您的 out 参数,我认为这不是参数,而是试图声明函数的 return类型;可以省略;作为参考,F# 中的函数 return 类型在函数签名后用冒号声明,例如 let f (x:int) : int = x + 5)


如果您更喜欢命令式风格,可以使用 seq 计算表达式。 yield 将产生单个元素,而 yield! 将产生另一个序列的每个元素的效果:

let rec flatten (e : System.Xml.Linq.XElement) = 
    seq {
        if e.HasElements then 
            for i in e.Elements() do 
                yield! flatten i
        else 
            yield e.Name.LocalName
    }