FSharp.SystemTextJson icon indicating copy to clipboard operation
FSharp.SystemTextJson copied to clipboard

How to upgrade from 0.17.4 for single case unions and skippable fields?

Open AlexGagne opened this issue 8 months ago • 4 comments

Hello, we were using the 0.17.4 version of the library and would like to upgrade it to the latest version of this library. However, we have a lot of serialized documents stored that can't be deserialized using the same options that we were using in 0.17.4.

Are we missing some configuration to deserialize as we did in 0.17.4? Or is this an instance of a bug?

Here's an example input that we cannot deserialize:

type SingleCaseUnion = SingleCaseUnion of string
    
type ToDeserialize =
    {
        SingleCaseUnion : SingleCaseUnion
        SkippableMember: int option
    }

let exampleBody = """
	{
		"singleCaseUnion": {
			"Case": "SingleCaseUnion",
			"Fields": [
				"some value"
			]
		}
    }"""

Here are the options that we used back then:

module Serializer =
    let options =
        let opt = JsonSerializerOptions(JsonSerializerDefaults.Web)
        let jsonUnionEncodings = JsonUnionEncoding.UnwrapFieldlessTags ||| JsonUnionEncoding.UnwrapOption
        opt.Converters.Add(JsonFSharpConverter(jsonUnionEncodings, unionTagCaseInsensitive = true))
        opt
    let deserialize<'a> (body : string) =
        JsonSerializer.Deserialize<'a> (body, options)

I see that SkippableOptionsField was added and is now required for the skippable member. If I'm not mistaken, We can use UnionAdjacentTag to deserialize the single case union.

I also see that we have to switch to the Fluent JsonFSharpOptions to be able to use SkippableOptionsField. So I tried with the following configuration:

module Serializer =
    let options =
        let opt =
            JsonFSharpOptions()
                .WithUnwrapOption()
                .WithUnionUnwrapFieldlessTags()
                .WithUnionTagCaseInsensitive()
                .WithAllowNullFields()
                .WithSkippableOptionFields()
                .WithUnionAdjacentTag()
                .ToJsonSerializerOptions()
        opt.Converters.Add(JsonFSharpConverter())
        opt
        
    let deserialize<'a> (body : string) =
        JsonSerializer.Deserialize<'a> (body, options)

However, we get this error:

System.Text.Json.JsonException: Missing field for record type Playground.Types+ToDeserialize: SingleCaseUnion
   at [email protected](String x)
   at System.Text.Json.Serialization.JsonRecordConverter`1.ReadRestOfObject(Utf8JsonReader& reader, Boolean skipFirstRead)
   at System.Text.Json.Serialization.JsonRecordConverter`1.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObject(Utf8JsonReader& reader, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadAsObject(Utf8JsonReader& reader, JsonTypeInfo jsonTypeInfo)
   at System.Text.Json.Serialization.JsonListConverter`1.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
   at Playground.Serializer.deserialize[a](String body) in C:\Users\AlexGagné\RiderProjects\Playground\Playground\Deserializer.fs:line 36
   at Playground.Program.main(String[] _arg1) in C:\Users\AlexGagné\RiderProjects\Playground\Playground\Program.fs:line 72

As if the AdjacentTag was being ignored? Or am I misunderstanding something?

AlexGagne avatar Jun 06 '24 20:06 AlexGagne