4
votes

So, I'm using the XML Type Provider to create types from an XML document.

One of the elements in the XML file has a Date property:

<Edit Date="06/30/2015 16:57:46"
      ... />

Which of course results in a Type like this:

type Edit = 
    inherit XmlElement

    member Date:  DateTime
    ...

Is there a way that I can add the following code:

 member this.LocalTime
    with get() =
        this.Date.ToLocalTime()

To the resulting Edit type?

The reason for this is that I'm binding to instances of Edit from XAML and I really don't want to write an IValueConverter just to do that.

Edit:

So, I just realized that these types do not make it to my XAML. Instead, I get instances of FSharp.Data.Runtime.BaseTypes.XmlElement which of course do not even contain the properties I see in F# code. I also need to consume these types from C# code, and even there I get only XmlElements without the properties

I know I could use XPath in XAML to navigate the XElements inside this, but I still need a way to access the resulting object model in a strongly typed way, both from C# and XAML.

Edit2:

So now I wrote a Type Extension like this:

type Catalog.Edit with
    member this.LocalTime with get() = this.Date.ToLocalTime()

And I see the member in F# code just like the generated members. However there's 2 drawbacks to this approach:

1 - It forced me to change my namespace into a module, which is less convenient since these types are all consumed from C# code and in there I see them as nested classes into the module class, which is ugly.

2 - I still can't see this member (nor the generated ones) from either C# nor XAML.

What's the correct way to implement this in the described scenario?

1
There are 2 types of TypeProviders in F#: generative and erasing. From C# (or another .NET language) you can use only types created by generative type providers. Types generated by erasing type provider could be consumed only from F# code (hence the name erasing I believe).Petr

1 Answers

6
votes

The F# Data type providers for XML and JSON are erasing type providers. This means that the types you see when you use them from F# do not actually exist at runtime - they are replaced with some underlying runtime representation (here XmlElement).

Unfortunately, this means that the types are not real types and so they are only visible to F#.

My recommendation is to define a simple domain type using F# records and load your data from the XML into your records. This is a bit more work, but it also gives you a bit more safety, because you are explicitly defining the structure - and so if your XML changes, you'll know you need to change your XAML code too!

It should be as easy as something like this:

type Edit = { Date : DateTime }

let GetData () = 
  let doc = MyXmlType.Load("somefile.xml")
  seq { for item in doc.Edits -> { Date = item.Date } }