F# - Deserialize JSON with Case-Insensitive properties (System.Text.Json)
Date: 2024-04-15 | create | tech | fsharp | json | dotnet |
JSON Serialization / Deserialization is a common plumbing task in modern web services. Dotnet's primary libraries for JSON handling are mostly geared towards C# so using them in F# is sometimes finnicky and lacking documentation.
In this post we'll go over a simple example of how to deserialize JSON in F# such that the cases of the JSON properties don't need to match the cases of your model.
Want to build fullstack F# web apps? Spin up a fullstack F# app in minutes with CloudSeed.
Deserialize JSON with F#
For this example we'll use a model and JSON with mismatched property cases.
- Model - All PascalCase (every word capitalized)
- JSON - Various cases to prove it handles them
Here we have a simple script that attempts to deserialize raw json into our data model.
We utilize JsonSerializerOptions
to configure System.Text.Json
's JsonSerializer
and the PropertyNameCaseInsensitive
option set to True to get the case-insensitive property behavior we want.
Code (also available on Replit)
open System
open System.Text.Json
type DeserializeType =
{
PascalCase: string
CamelCase: string
AllCapsCase: string
AlternatingCapsCase: string
SnakeCase: string // HAM: This won't map cause difference is not capitalization
}
[<EntryPoint>]
let main argv =
printfn "Hello World from F#!"
let jsonString = @"{
""PascalCase"": ""iampascalcase"",
""camelCase"": ""iamcamelcase"",
""ALLCAPSCASE"": ""iamallcapscase"",
""AlTeRnAtInGcApScAsE"": ""iamalternatingcapscase"",
""snake_case"": ""iamsnakecase""
}"
let options: JsonSerializerOptions =
(JsonSerializerOptions())
options.PropertyNameCaseInsensitive <- true
let data = JsonSerializer.Deserialize<DeserializeType>(jsonString, options)
printfn "Deserialized Data: %A" data
0 // return an integer exit code
Output:
Hello World from F#!
Deserialized Data: { PascalCase = "iampascalcase"
CamelCase = "iamcamelcase"
AllCapsCase = "iamallcapscase"
AlternatingCapsCase = "iamalternatingcapscase"
SnakeCase = null }
We can see that each property is correctly mapped EXCEPT for SnakeCase
.
The reason is:
- Property
SnakeCase
differs from JSONsnake_case
by a character, not a case thus doesn't get mapped
Take note that this is dangerous - our model says it does not accept nulls yet here we have a silent failure which causes our non-nullable data model to contain a null! This is a very easy way to introduce hard-to-track bugs as now our type system is lying to us.
Next
F# is an incredible (and fun!) programming language but there are often gotchas at the edges where / when it tries to integrate with other technologies with different defaults. Always be careful when integrating with C# / dotnet in particular as they tend to like mutable OO and nullable values.
Q: How are you handling JSON in your F# codebase? What strategies do you use to make it safe and sane?
If you liked this post you might also like:
Want more like this?
The best / easiest way to support my work is by subscribing for future updates and sharing with your network.