F# – Serializing F# Record and Discriminated Union types

Yan Cui

I help clients go faster for less using serverless technologies.

This article is brought to you by

I never fully recovered my workspace setup when I upgraded my laptop two years ago, and I still miss things today. If only I had known about Gitpod back then…

Learn more

I love using F#’s Record and Discriminated Union types, they work nicely with pattern matching inside your F# code and can often alleviate some of the ceremony involved around creating and using a complex object hierarchy.

However, on the odd occasion when you need to serialize them into JSON/XML/Binary format, it might not be immediately obvious what you need to do to achieve that goal.

In general, I avoid using F# specific types when I require interoperability with C# and/or support for transport as it’s just much more easily done with standard CLR types. However, in case that’s not an option, I hope this post will give you a summary of the different ways you can prepare your Record/DU types for transport.

Record

To serialize a Record type using the DataContractSerializer (for XML) or DataContractJsonSerializer (for JSON), simply decorate your Record type with DataContractAttribute and DataMemberAttribute like you normally would:

image

After that you can serialize/deserialize a Person instance normally although the serialized XML/JSON would not be as readable as with other CLR types:

JSON

Record:

{“Age@”:99,”Name@”:”Yan Cui”}

Class:

{ “Age” : 99, “Name” : “Yan Cui” }

XML

Record:

<FSI_0002.Person xmlns=”http://schemas.datacontract.org/2004/07/”

                            xmlns:i=”‘http://www.w3.org/2001/XMLSchema-instance”>

    <Age_x0040_>99</Age_x0040_>

    <Name_x0040_>Yan Cui</Name_x0040_>

</FSI_0002.Person>

Class:

<Person xmlns=”http://schemas.datacontract.org/2004/07/ClassLibrary1”

             xmlns:i=”http://www.w3.org/2001/XMLSchema-instance”>

    <Age>99</Age>

    <Name>Yan Cui</Name>

</Person>

As for binary serialization, a Record type is marked with the Serializable attribute by default, which means it can be serialized with the BinaryFormatter. This is how the Person record type looks in reflector:

image

Discriminated Unions

A single case Discriminated Union such as the one below is serializable by DataContractSerializer, DataContractJsonSerializer and BinaryFormmatter out of the box.

image

The JSON and XML output for an instance of this DU looks like this:

JSON :

{“item”:”test”}

XML   :

<SingleCaseDU xmlns=”http://schemas.datacontract.org/2004/07/”

                        xmlns:i=”http://www.w3.org/2001/XMLSchema-instance”>

    <item>test</item>

</SingleCaseDU>

A multi-case Discriminated Union on the other hand, requires a little more work.

image

Whilst it works with BinaryFormatter by default, it will not with DataContractSerializer and DataContractJsonSerializer. If you try to serialize an instance of MultiCaseDU using either you will get a SerializationException saying that the type MultiCaseDU.Case1 or MultiCaseDU.Case2 is not expected.

So to make the MultiCaseDU type serializable with both DataContractSerializer and DataContractJsonSerializer you need to supply the nested types (Case1 and Case2) to the known type resolver. Something like this would do the trick:

image

After that, you’ll be able to serialize instances of MultiCaseDU:

JSON

Case1:

{“__type”:”MultiCaseDU.Case1:#FSI_0002.Contracts”,”item”:”test”}

Case2:

{“__type”:”MultiCaseDU._Case2:#FSI_0002.Contracts”}

XML

Case1:

<MultiCaseDU i:type=”MultiCaseDU.Case1″

                     xmlns=”http://schemas.datacontract.org/2004/07/”

                     xmlns:i=”http://www.w3.org/2001/XMLSchema-instance”>

    <item>test</item>

</MultiCaseDU>

Case2:

<MultiCaseDU i:type=”MultiCaseDU._Case2″

                     xmlns=”http://schemas.datacontract.org/2004/07/”

                     xmlns:i=”http://www.w3.org/2001/XMLSchema-instance”/>

 

 

 

For the full example, including serialization and deserialization code, see this gist.

Whenever you’re ready, here are 3 ways I can help you:

  1. Production-Ready Serverless: Join 20+ AWS Heroes & Community Builders and 1000+ other students in levelling up your serverless game. This is your one-stop shop for quickly levelling up your serverless skills.
  2. I help clients launch product ideas, improve their development processes and upskill their teams. If you’d like to work together, then let’s get in touch.
  3. Join my community on Discord, ask questions, and join the discussion on all things AWS and Serverless.

2 thoughts on “F# – Serializing F# Record and Discriminated Union types”

  1. Pingback: F# – XmlSerializer, Record types and [CLIMutable] | theburningmonk.com

  2. Pingback: Contrasting F# and Elm’s record types | theburningmonk.com

Leave a Comment

Your email address will not be published. Required fields are marked *