Logo




Subscribe:
RSS 2.0 | Atom 1.0
Categories:

Sign In


[Giagnocavo]Michael::Write()

# Tuesday, November 25, 2008
XmlSerialization for F# Types
On a current project, I'm using F# with ASMX Web Services (Mono, so no WCF). I started off declaring some types like this:

type Account() =
    [<DefaultValue>]
    val mutable Name : string
    [<DefaultValue>]
    val mutable Data : byte array


We have to tag "default value" to tell F# it should go generate the default (Unchecked.defaultof<'a>) -- in this case, null.

Unfortunately, the serialized XML is not what we want. Currently F# generates both a public field ("_Name") and a get/set property ("Name") to access it. I'm not sure why this is, but there's probably some interesting reason (the part of the spec is 10.2.7 Explicit Fields, but it doesn't mention the implementation). The workaround is simple: tag the field with "XmlIgnore".

As of F# 1.9.3.7, you can choose to target an attribute at the property or the field for a val in a class type. See: http://blogs.msdn.com/dsyme/archive/2007/11/30/full-release-notes-for-f-1-9-3-7.aspx (I didn't find this part in the spec.)

So, the revised declaration for the XmlSerialization-friendly type is:

type Account() =
    [<DefaultValue; field: XmlIgnore>]
    val mutable Name : string
    [<DefaultValue; field: XmlIgnore>]
    val mutable Data : byte array


I found that to add a lot of noise, so I aliased them:

type DV = DefaultValueAttribute
type XI = XmlIgnoreAttribute

type Account() =
    [<DV; field: XI>] val mutable Name : string
    [<DV; field: XI>] val mutable Data : byte array


Now it works just fine and creates the XML we'd expect.

One other thing might bite you: null. F# rightfully eschews null. If you have a function that returns one of these types, you'll find it won't let you return null:

> let test() : Account = null;;
  let test() : Account = null;;
  -----------------------^^^^^
stdin(6,24): error FS0043: The type 'Account' does not have 'null' as a proper value.

This is generally good, but unfortunately, interop with the unenlightened world is sometimes necessary. The right way to do this is:

let test() : Account = Unchecked.defaultof<Account>;;

That's a bit too verbose for my liking, since I have to go annotate the type. A simple function will get around that:

> let inline getnull() = Unchecked.defaultof<_>;;
val inline getnull : unit -> 'a

> let test() : Account = getnull();;
val test : unit -> Account

Yes, I know it's not null, but default, but I think the purpose is clear enough.

FSharp
Tuesday, November 25, 2008 2:16:33 AM UTC  #    Comments [0]  |  Trackback

OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview