I'm trying to build a dynamic type/class builder for C# using F#, from the following 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>
Using the code below I can parse the sample just fine
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>
The problem now is that I can't get my head around retrieving the inner parts of the about_content tag.
After parsing the actual xml using
let parsedValue = Configuration.Parse(xmlIn)
I've tried to get my head around the recursion handling in F# but am stuck at the non-working code that looks like this (e would be 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
What I would need is a hint on how to gather the e.Name.LocalName values into a sequence/List as a result of the recursion. I could also live with having a list of XElements at the end.
The function
flattenneeds to return a sequence, not a single thing.For elements with subelements, you need to call
flattenfor each, then concat all results:(note that
XElement.Elementsis a method, not a property; therefore, you need to add()to call it)For a single element, just return its name wrapped in a single-element sequence:
Putting it all together:
(also note that I have removed your
outparameter, which, I assume, was meant to be not a parameter, but an attempt to declare the function's return type; it can be omitted; for reference, function return type in F# is declared after the function's signature with a colon, e.g.let f (x:int) : int = x + 5)If you prefer a more imperative-looking style, you can use the
seqcomputation expression.yieldwill yield a single element, whileyield!will have the effect of yielding each element of another sequence: