Как я могу рекурсивно получить все дочерние элементы XElement для XmlProvider - PullRequest
0 голосов
/ 13 сентября 2018

Я пытаюсь построить динамический конструктор типов / классов для C #, используя F #, из следующего 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)

I 'мы пытались разобраться с обработкой рекурсии в 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 Ответ

0 голосов
/ 13 сентября 2018

Функция 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
    }
...