F# – Serializing F# Record and Discriminated Union types

Check out my new course Learn you some Lambda best practice for great good! and learn the best practices for performance, cost, security, resilience, observability and scalability.

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.

Liked this article? Support me on Patreon and get direct help from me via a private Slack channel or 1-2-1 mentoring.
Subscribe to my newsletter


Hi, I’m Yan. I’m an AWS Serverless Hero and the author of Production-Ready Serverless.

I specialise in rapidly transitioning teams to serverless and building production-ready services on AWS.

Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help.

Hire me.


Check out my new podcast Real-World Serverless where I talk with engineers who are building amazing things with serverless technologies and discuss the real-world use cases and challenges they face. If you’re interested in what people are actually doing with serverless and what it’s really like to be working with serverless day-to-day, then this is the podcast for you.


Check out my new course, Learn you some Lambda best practice for great good! In this course, you will learn best practices for working with AWS Lambda in terms of performance, cost, security, scalability, resilience and observability. We will also cover latest features from re:Invent 2019 such as Provisioned Concurrency and Lambda Destinations. Enrol now and start learning!


Check out my video course, Complete Guide to AWS Step Functions. In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. There is something for everyone from beginners to more advanced users looking for design patterns and best practices. Enrol now and start learning!


Are you working with Serverless and looking for expert training to level-up your skills? Or are you looking for a solid foundation to start from? Look no further, register for my Production-Ready Serverless workshop to learn how to build production-grade Serverless applications!

Find a workshop near you