Yan Cui
I help clients go faster for less using serverless technologies.
This article is brought to you by
Don’t reinvent the patterns. Catalyst gives you consistent APIs for messaging, data, and workflow with key microservice patterns like circuit-breakers and retries for free.
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:
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:
Discriminated Unions
A single case Discriminated Union such as the one below is serializable by DataContractSerializer, DataContractJsonSerializer and BinaryFormmatter out of the box.
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.
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:
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:
- 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.
- 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.
- Join my community on Discord, ask questions, and join the discussion on all things AWS and Serverless.
Pingback: F# – XmlSerializer, Record types and [CLIMutable] | theburningmonk.com
Pingback: Contrasting F# and Elm’s record types | theburningmonk.com