F# - Deserialize JSON with Case-Insensitive properties (System.Text.Json)
Date: 2024-04-15 | create | dotnet | fsharp | json | tech |
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
SnakeCasediffers from JSONsnake_caseby 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 way to support my work is to like / comment / share for the algorithm and subscribe for future updates.
