代码之家  ›  专栏  ›  技术社区  ›  Markus Deibel

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

  •  2
  • Markus Deibel  · 技术社区  · 7 年前

    我正在尝试使用F#,从以下XML为C#构建一个动态类型/类生成器

    <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 标签。

    在使用

    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 值作为递归的结果放入序列/列表中。我也可以忍受有一张 XElement 在最后。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Fyodor Soikin    7 年前

    函数 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
        }