Question

I have a scenario where I am using the XML type provider from FSharp.Data to read in a stream containing various key/value pairs. The values in this case are sometimes decimals, sometimes dates, and sometimes strings:

<records>
  <fields>
    <key>foo</key>
    <value>123.456</value>
  </fields>
  <fields>
    <key>bar</key>
    <value>2013-07-23</value>
  </fields>
  <fields>
    <key>fizz</key>
    <value>hello world</value>
  </fields>
</records>

Because there seems to be no way to refer to the provided types by name, in order to pattern match on the values I have to move the values into a tuple and then provide active patterns over that:

open System
open System.IO
type XmlFoo=FSharp.Data.XmlProvider<"""<records>
      <fields>
        <key>foo</key>
        <value>123.456</value>
      </fields>
      <fields>
        <key>bar</key>
        <value>2013-07-23</value>
      </fields>
      <fields>
        <key>fizz</key>
        <value>hello world</value>
      </fields>
    </records>""">

[<EntryPoint>]
let main argv = 

    let (|Date|_|) ((v,_,_):Option<DateTime> * Option<Decimal> * Option<String>) = v
    let (|Number|_|) ((_,v,_):Option<DateTime> * Option<Decimal> * Option<String>) = v
    let (|String|_|) ((_,_,v):Option<DateTime> * Option<Decimal> * Option<String>) = v

    use stream = File.OpenRead("sample.xml")
    let data = XmlFoo.Load(stream)

    for field in data.Fields do
      let value = field.Value.DateTimeValue, field.Value.NumberValue, field.Value.StringValue
      match value with
      | Date x -> printfn "Found a date: %s" (x.ToShortDateString())
      | Number x -> printfn "Found a number: %M" x
      | String x -> printfn "Found a string: %s" x
      | _ -> printfn "Found nothing"

    0 // return an integer exit code

Is there a way either to a) refer to the provided types directly (so I don't need the intermediate tuple and the active patterns can be generalised to XML files that return any number of possible data types) or b) modify the type provider so that it provides the active patterns itself - not sure if this is possible looking at the type provider API.

EDIT: Nevermind, I missed the obvious - the tooltips in VS2013 are misleading. By displaying <...> it gives the impression there isn't a way to refer to the type by name. Thanks to @ovatsus for the tip. You can do this:

open System
open System.IO
type XmlFoo=FSharp.Data.XmlProvider<"""<records>
      <fields>
        <key>foo</key>
        <value>123.456</value>
      </fields>
      <fields>
        <key>bar</key>
        <value>2013-07-23</value>
      </fields>
      <fields>
        <key>fizz</key>
        <value>hello world</value>
      </fields>
    </records>""">

[<EntryPoint>]
let main argv = 

    let (|Date|_|) (v:XmlFoo.Value) = v.DateTimeValue
    let (|Number|_|) (v:XmlFoo.Value) = v.NumberValue
    let (|String|_|) (v:XmlFoo.Value) = v.StringValue

    use stream = File.OpenRead("sample.xml")
    let data = XmlFoo.Load(stream)

    for field in data.Fields do
      match field.Value with
      | Date x -> printfn "Found a date: %s" (x.ToShortDateString())
      | Number x -> printfn "Found a number: %M" x
      | String x -> printfn "Found a string: %s" x
      | _ -> printfn "Found nothing"

    0 // return an integer exit code
Was it helpful?

Solution

You can create type aliases to reference the types directly:

type F = XmlFoo.Field

OTHER TIPS

For the question about having active patterns directly in the type provider, it seems that active patterns must be let bindings, not class members (even static), so there is no way to provide them unfortunately.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top