Closed davedawkins closed 8 months ago
Perhaps something like this?
#r "nuget: Thoth.Json.Net, 11.0"
type Thing = { foo : string[]; bar : string [] }
module Thing =
open Thoth.Json.Net
let decode : Decoder<Thing> =
Decode.object
(fun get ->
{
foo = get.Required.Field "foo" (Decode.array Decode.string)
bar =
get.Optional.Field "bar" (Decode.array Decode.string)
|> Option.defaultValue [||]
})
Hello @davedawkins,
I am not sure to understand everything. Could you please provide the expected values associated to the JSON?
For example:
type Thing = { foo : string[]; bar : string [] }
let json =
"""
{
"foo": [ "cat", "dog" ]
}
"""
let expected =
{
foo = [ "cat", "dog" ]
bar = []
}
If there are different scenario I would be interested in having them too.
Hello @davedawkins,
I am not sure to understand everything. Could you please provide the expected values associated to the JSON? ... If there are different scenario I would be interested in having them too.
type Thing = { foo : string[]; bar : string [] } // bar is a new field for Thing v2
let json1 = // Legacy Thing v1 data
"""
{
"foo": [ "cat", "dog" ] // Must now target Thing.bar
}
"""
let json2 = // Example Thing v2 data
"""
{
"foo": [ "apple", "peach" ]
"bar": [ "cat", "dog" ]
}
"""
let expectedFromJson1 =
{
foo = []
bar = [ "cat", "dog" ]
}
let expectedFromJson2 =
{
foo = [ "apple", "peach" ]
bar = [ "cat", "dog" ]
}
The rule is:
@njlr Thank you, but it misses the JSON "foo" to Thing.bar mapping
Please don't judge me for creating this upgrade scenario....
Thank you!
My best effort
type Thing_v1 = { foo : string[] }
let decoderThing : Decoder<Thing> =
fun path value ->
let r =
if (JsHelpers.jsPropertyExists("bar",value)) then
Decode.Auto.fromString<Thing>( Encode.Auto.toString(value))
else
match Decode.Auto.fromString<Thing_v1>( Encode.Auto.toString(value)) with
| Ok tv1 -> Ok ({ foo = [||]; bar = tv1.foo })
| Error _ as e -> e
match r with
| Ok v -> Ok v
| Error msg -> Error <| DecoderError (msg, ErrorReason.FailMessage msg)
I think you want Decode.oneOf
let decoderThing : Decoder<Thing> =
Decode.oneOf
[
decoderV2
decoderV1
]
Beware that the order matters here!
It will attempt decoderV2
first and only try decoderV1
if that fails.
You can also use Decode.map
if you need to do a transformation:
let decoderThing : Decoder<Thing> =
Decode.oneOf
[
decoderV2
decoderV1 |> Decode.map convertV1ToV2
]
Beautiful thank you
Can confirm this works, thank you so much
let decoder : Decoder<EntityDTO> =
Decode.oneOf [
Decode.Auto.generateDecoder<EntityDTO>()
Decode.Auto.generateDecoder<EntityDTO_v1>() |> Decode.map mapE1E2
]
I have this JSON on disk
which maps to type
I have changed Thing to be
and now the
foo
on disk must go intoThing.bar
(andThing.foo
will be empty)I know how to handle
bar
being missing, by using custom decoders, but cannot figure out how to write aThing
decoder which can readfoo
intoThing.bar
ifbar
is missing in the JSON.Thank you