F# – Serializing F# Record and Discriminated Union types

You can become a serverless blackbelt. Enrol to my 4-week online workshop Production-Ready Serverless and gain hands-on experience building something from scratch using serverless technologies. At the end of the workshop, you should have a broader view of the challenges you will face as your serverless architecture matures and expands. You should also have a firm grasp on when serverless is a good fit for your system as well as common pitfalls you need to avoid. Sign up now and get 15% discount with the code yanprs15!

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 I help companies go faster for less by adopting serverless technologies successfully.

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.


Skill up your serverless game with this hands-on workshop.

My 4-week Production-Ready Serverless online workshop is back!

This course takes you through building a production-ready serverless web application from testing, deployment, security, all the way through to observability. The motivation for this course is to give you hands-on experience building something with serverless technologies while giving you a broader view of the challenges you will face as the architecture matures and expands.

We will start at the basics and give you a firm introduction to Lambda and all the relevant concepts and service features (including the latest announcements in 2020). And then gradually ramping up and cover a wide array of topics such as API security, testing strategies, CI/CD, secret management, and operational best practices for monitoring and troubleshooting.

If you enrol now you can also get 15% OFF with the promo code “yanprs15”.

Enrol now and SAVE 15%.


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!