Some sample types
type PieceOfText = { Text: string }
type LengthOfPipe = { Length: int }
type Junk =
| PieceOfText of PieceOfText
| LengthOfPipe of LengthOfPipe
Set up some options so that union tag names and record field names use snake_lower_case.
let makeOptions namingPolicy =
JsonFSharpOptions.Default()
.WithUnionTagNamingPolicy(namingPolicy)
.WithUnionInternalTag()
.WithUnionUnwrapRecordCases()
.WithAllowOverride()
.ToJsonSerializerOptions(PropertyNamingPolicy = namingPolicy)
let options = makeOptions JsonNamingPolicy.SnakeCaseLower
Try out some serialization
let value = PieceOfText { Text = "Hello" }
let json = JsonSerializer.Serialize (value, options)
val json: string = "{"Case":"piece_of_text","text":"Hello"}"
So far, so good.
Now use [<JsonFSharpConverter>] to specify the tag name:
[<JsonFSharpConverter (UnionTagName = "type")>]
type Junk =
| PieceOfText of PieceOfText
| LengthOfPipe of LengthOfPipe
let value = PieceOfText { Text = "Hello" }
let json = JsonSerializer.Serialize (value, options)
val json: string = "{"type":"PieceOfText","text":"Hello"}"
The union tag value is no longer converted to snake_lower_case.
OK, let's try a workaround, which is to set UnionTagNamingPolicy on the attribute.
[<JsonFSharpConverter (UnionTagName = "type", UnionTagNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower)>]
type Junk =
| PieceOfText of PieceOfText
| LengthOfPipe of LengthOfPipe
let value = PieceOfText { Text = "Hello" }
let json = JsonSerializer.Serialize (value, options)
System.Reflection.CustomAttributeFormatException: 'UnionTagNamingPolicy' property specified was not found.
---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.Exception: Unknown naming policy: SnakeCaseLower
at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1448.Invoke(String message)
at System.Text.Json.Serialization.JsonFSharpConverterAttribute.set_UnionTagNamingPolicy(JsonKnownNamingPolicy v) in /home/runner/work/FSharp.SystemTextJson/FSharp.SystemTextJson/src/FSharp.SystemTextJson/All.fs:line 108
at InvokeStub_JsonFSharpConverterAttribute.set_UnionTagNamingPolicy(Object, Span`1)
at System.Reflection.RuntimeMethodInfo.InvokePropertySetter(Object obj, BindingFlags invokeAttr, Binder binder, Object parameter, CultureInfo culture)
--- End of inner exception stack trace ---
at System.Reflection.RuntimeMethodInfo.InvokePropertySetter(Object obj, BindingFlags invokeAttr, Binder binder, Object parameter, CultureInfo culture)
at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
--- End of inner exception stack trace ---
at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
at System.Text.Json.Serialization.Helpers.overrideOptions(Type ty, JsonFSharpOptions defaultOptions) in /home/runner/work/FSharp.SystemTextJson/FSharp.SystemTextJson/src/FSharp.SystemTextJson/Helpers.fs:line 143
at System.Text.Json.Serialization.JsonUnionConverter.CreateConverter(Type typeToConvert, JsonSerializerOptions options, JsonFSharpOptions fsOptions) in /home/runner/work/FSharp.SystemTextJson/FSharp.SystemTextJson/src/FSharp.SystemTextJson/Union.fs:line 846
at System.Text.Json.Serialization.JsonConverterFactory.GetConverterInternal(Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.JsonSerializerOptions.ExpandConverterFactory(JsonConverter converter, Type typeToConvert)
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetConverterForType(Type typeToConvert, JsonSerializerOptions options, Boolean resolveJsonConverterAttribute)
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type type)
at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, CachingContext context)
--- End of stack trace from previous location ---
at System.Text.Json.JsonSerializerOptions.CachingContext.CacheEntry.GetResult()
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType)
at System.Text.Json.JsonSerializerOptions.GetTypeInfoForRootType(Type type, Boolean fallBackToNearestAncestorType)
at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
at <StartupCode$FSI_0041>.$FSI_0041.main@() in /home/david/code/EditorPlay/stdin:line 3
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
Stopped due to error
The exception comes from here:
static let namingPolicy =
function
| JsonKnownNamingPolicy.Unspecified -> null
| JsonKnownNamingPolicy.CamelCase -> JsonNamingPolicy.CamelCase
| p -> failwithf "Unknown naming policy: %A" p
If I use UnionTagNamingPolicy = JsonKnownNamingPolicy.CamelCase, the exception does not occur, but that's not the naming policy I want.
One workaround is to use [<JsonName>] on each union case to explicitly set the snake_lower_case result I want
[<JsonFSharpConverter (UnionTagName = "type")>]
type Junk =
| [<JsonName "piece_of_text">] PieceOfText of PieceOfText
| [<JsonName "length_of_pipe">] LengthOfPipe of LengthOfPipe
let value = PieceOfText { Text = "Hello" }
let json = JsonSerializer.Serialize (value, options)
val json: string = "{"type":"piece_of_text","text":"Hello"}"
This works, but it's a bit tedious and error prone.
Some sample types
Set up some options so that union tag names and record field names use snake_lower_case.
Try out some serialization
So far, so good.
Now use
[<JsonFSharpConverter>]to specify the tag name:The union tag value is no longer converted to snake_lower_case.
OK, let's try a workaround, which is to set
UnionTagNamingPolicyon the attribute.The exception comes from here:
If I use
UnionTagNamingPolicy = JsonKnownNamingPolicy.CamelCase, the exception does not occur, but that's not the naming policy I want.One workaround is to use
[<JsonName>]on each union case to explicitly set the snake_lower_case result I wantThis works, but it's a bit tedious and error prone.