Pregunta

I want to retrieve a and b attributes of let's say (level2 name=) level2Name4 in (level1 name=) level1Name2 using the F# Data's XML Type Provider from the following XML:

<?xml version="1.0" encoding="UTF-16"?>
<ROOT>
    <level1 name="level1Name1">
        <level2 name="level2Name1" a="8" b="104" />
        <level2 name="level2Name2" a="85" b="140" />
    </level1>
    <level1 name="level1Name2">
        <level2 name="level2Name3" a="50" b="500" />
        <level2 name="level2Name4" a="376" b="1065" />
        <level2 name="level2Name5" a="10" b="10" />
        <level2 name="level2Name6" a="700" b="700" />
    </level1>
    <level1 name="level1Name3">
        <level2 name="level2Name7" a="502" b="66" />
    </level1>
</ROOT>

However, I can't transform / expand level2 into a map, nor to retrieve the necessary information from there:

open System.Xml.Linq
open FSharp.Data

type myXmlType = XmlProvider<"""<?xml version="1.0" encoding="UTF-16"?><ROOT><level1 name="level1Name1"><level2 name="level2Name1" a="8" b="104" /><level2 name="level2Name2" a="85" b="140" /></level1><level1 name="level1Name2"><level2 name="level2Name3" a="50" b="500" /><level2 name="level2Name4" a="376" b="1065" /><level2 name="level2Name5" a="10" b="10" /><level2 name="level2Name6" a="700" b="700" /></level1><level1 name="level1Name3"><level2 name="level2Name7" a="502" b="66" /></level1></ROOT>""">

let myXml = myXmlType.Parse("""<?xml version="1.0" encoding="UTF-16"?><ROOT><level1 name="level1Name1"><level2 name="level2Name1" a="8" b="104" /><level2 name="level2Name2" a="85" b="140" /></level1><level1 name="level1Name2"><level2 name="level2Name3" a="50" b="500" /><level2 name="level2Name4" a="376" b="1065" /><level2 name="level2Name5" a="10" b="10" /><level2 name="level2Name6" a="700" b="700" /></level1><level1 name="level1Name3"><level2 name="level2Name7" a="502" b="66" /></level1></ROOT>""")

let myA, myB =
    myXml.GetLevel1s()
    |> Seq.filter (fun L1 -> L1.Name = "level1Name2")
    |> Seq.initInfinite (fun i L1 -> L1.GetLevel2s())
    ...

This is where I am stuck. How to unfold / expand / transform level2 here? And then, how to retrieve a and b of(level2 name=) level2Name4 in (level1 name=) level1Name2?

¿Fue útil?

Solución

You were close. The GetXXX() methods return arrays, so you need to flatten the inner level. Instead of Seq.initInfinite, which takes an generator function (int -> 'T), you could either use a combination of Seq.mapand Seq.concat, or Seq.collect to reduce the nesting by one level:

myXmlType.GetSample().GetLevel1s()
|> Seq.filter (fun L1 -> L1.Name = "level1Name2")
|> Seq.collect (fun L1 -> L1.GetLevel2s())
|> Seq.iter (fun L2 -> printfn "%A %A" L2.A L2.B)

which prints

50 500
376 1065
10 10
700 700

Edit So you're not interested in the attribute's sequence, but want the attributes of one specific instance as determined by its name? Then we have to search for the element, first in the outer level (may fail), than the inner (may fail too).

let myA, myB =
    myXmlType.GetSample().GetLevel1s()
    |> Seq.tryFind (fun L1 -> L1.Name = "level1Name2") 
    |> function
    | None -> failwith "level1Name2 not found"
    | Some L1 ->
        L1.GetLevel2s()
        |> Seq.tryFind (fun L2 -> L2.Name = "level2Name4")
        |> function
        | None -> failwith "level2Name4 not found"
        | Some L2 -> L2.A, L2.B
// val myB : int = 1065
// val myA : int = 376

Edit2 You can combine the two approaches:

let myA, myB =
    myXmlType.GetSample().GetLevel1s()
    |> Seq.filter (fun L1 -> L1.Name = "level1Name2")
    |> Seq.collect (fun L1 -> L1.GetLevel2s())
    |> Seq.tryFind (fun L2 -> L2.Name = "level2Name4")
    |> function
    | None -> failwith "level2Name4 not found"
    | Some L2 -> L2.A, L2.B
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top