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

问题描述 投票:2回答:1

我正在尝试使用F#为C#构建动态类型/类构建器,来自以下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值作为递归的结果收集到序列/ List中。我也可以忍受在最后有一个XElements列表。

recursion f# f#-data fsharp.data.typeproviders
1个回答
3
投票

函数flatten需要返回序列,而不是单个事物。

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

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

(请注意,XElement.Elements是一种方法,而不是属性;因此,您需要添加()来调用它)

对于单个元素,只需返回包含在单个元素序列中的名称:

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参数,我认为这个参数不是参数,而是试图声明函数的返回类型;它可以省略;作为参考,F#中的函数返回类型是在带冒号的函数签名,例如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
    }
© www.soinside.com 2019 - 2024. All rights reserved.