Closed RicoSaupeBosch closed 1 year ago
Hello,
Required.At
is for decoding arrays if you look at the error message it is explaining you that it was expecting a list but found an object.
If you want to decode an object, you want to use Required.Field
.
When writing code in marking, you can use ``` around your code block to make the format easier to read.
This a code block with no language associated to it
```fs
// Here you are inside an F# code block
Here is working snippet for the JSON you provided:
open Fable.Core
open Thoth.Json
let json =
"""
{
"state": {
"case": "BlankMigrationState",
"fields": [
{
"case": "New"
}
]
}
}
"""
type BlankState =
| New
| ImpedimentCheck
module BlankState =
let decoder : Decoder<BlankState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
match caseValue with
| "New" -> Decode.succeed New
| invalidCase ->
sprintf "'%s' is invalid case for BlankState" invalidCase
|> Decode.fail
)
type RequestState =
| BlankMigrationState of BlankState
module RequestState =
let decoder : Decoder<RequestState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
match caseValue with
| "BlankMigrationState" ->
Decode.field "fields" (Decode.index 0 BlankState.decoder)
|> Decode.map BlankMigrationState
| invalidCase ->
sprintf "'%s' is invalid case for RequestState" invalidCase
|> Decode.fail
)
let result =
Decode.unsafeFromString
(Decode.field "state" RequestState.decoder)
json
JS.console.log result
Thanks for the example. If I use your code with Thoth.Json.Net then it works fine. I am using Thoth.Json and the json from the api looks like that.
{ "modifiedBy": "name",
"state": {
"case": "BlankMigrationState",
"fields": [
{
"case": "New"
}
]
}
fs code is
let blankStateDecoder : Decoder<BlankState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
let value = fromStringI<BlankState> caseValue
match value with
| Some v -> Decode.succeed v
| None -> $"{caseValue} is invalid case for BlankState" |> Decode.fail)
let requestStateDecoder : Decoder<RequestState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
match caseValue with
| "BlankMigrationState" ->
Decode.field "fields" (Decode.index 0 blankStateDecoder)
|> Decode.map RequestState.BlankMigrationState
| invalidCase -> $"{invalidCase} is invalid case for RequestState" |> Decode.fail)
type BlankState =
| New
| ImpedimentCheck
type RequestState =
| BlankMigrationState of BlankState
type Request =
{ ModifiedBy: string
mutable State: RequestState }
let RequestDecoder: Decoder<Request> =
Decode.object(fun fields -> {
ModifiedBy= fields.Required.Field "modifiedBy" Decode.string
State = fields.Required.Field "state" requestStateDecoder
})
Still I get the same error. Did I get your solution wrong?
@RicoSaupeBosch Without the full code I can't help you.
For example, the fromStringI
function is something custom and I don't know the definition of it.
The error is explaining you what it is expecting and what it got so you need to check if you didn't make a type in the decoder definition or pass the wrong JSON/value tc.
Yes. that is correct. This function gets a DU option from a string
Please use code block when sharing code, as it allow copy/paste.
I tried to run the code you provided me with, and I don't have the same error as you do:
Code run:
module Main
open Feliz
open Browser.Dom
open Fable.Core
open Thoth.Json
open FSharp.Reflection
open System.Text
open System
let json =
"""
{ "modifiedBy": "name",
"state": {
"case": "BlankMigrationState",
"fields": [
{
"case": "New"
}
]
}
}
"""
type BlankState =
| New
| ImpedimentCheck
type RequestState =
| BlankMigrationState of BlankState
let inline fromStringI<'a> (s : string) =
let unionCases =
FSharpType.GetUnionCases typeof<'a>
|> Array.filter (fun case ->
StringComparer.CurrentCultureIgnoreCase.Equals(case.Name, s)
)
match unionCases with
| [| case |] -> Some(FSharpValue.MakeUnion(case, [||]) :?> 'a)
| _ -> None
let blankStateDecoder : Decoder<BlankState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
let value = fromStringI<BlankState> caseValue
match value with
| Some v -> Decode.succeed v
| None -> sprintf "%s is invalid case for BlankState" caseValue |> Decode.fail)
let requestStateDecoder : Decoder<RequestState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
match caseValue with
| "BlankMigrationState" ->
Decode.field "fields" (Decode.index 0 blankStateDecoder)
|> Decode.map RequestState.BlankMigrationState
| invalidCase -> sprintf "%s is invalid case for RequestState" caseValue |> Decode.fail)
type Request =
{ ModifiedBy: string
mutable State: RequestState }
let RequestDecoder: Decoder<Request> =
Decode.object(fun fields -> {
ModifiedBy= fields.Required.Field "modifiedBy" Decode.string
State = fields.Required.Field "state" requestStateDecoder
})
let result =
Decode.unsafeFromString RequestDecoder json
JS.console.log result
Result:
Error at: `$.state.fields.[0]`
The following `failure` occurred with the decoder: New is invalid case for BlankState
at unsafeFromString (Decode.fs.js:128:15)
at Main.fs.js?t=1683297937460:91:23
u
Also if you look at the Fable output you will see that there is compilation warning/error because StringComparer
is not supported by Fable
./Main.fs(38,12): (38,51) error FABLE: System.StringComparer.get_CurrentCultureIgnoreCase (static) is not supported by Fable - Inline call from .(48,20)
./Main.fs(38,12): (38,72) error FABLE: System.StringComparer.Equals is not supported by Fable - Inline call from .(48,20)
Fable compilation finished in 13ms
./Main.fs(1,1): warning FABLE: Fable only supports a subset of standard .NET API, please check https://fable.io/docs/dotnet/compatibility.html. For external libraries, check whether they are Fable-compatible in the package docs.
Changing the comparaison to case.Name.ToLowerInvariant() = s.ToLowerInvariant()
make the code works on my side:
Full working snippet that I used:
module Main
open Feliz
open Browser.Dom
open Fable.Core
open Thoth.Json
open FSharp.Reflection
open System.Text
open System
let json =
"""
{ "modifiedBy": "name",
"state": {
"case": "BlankMigrationState",
"fields": [
{
"case": "New"
}
]
}
}
"""
type BlankState =
| New
| ImpedimentCheck
type RequestState =
| BlankMigrationState of BlankState
let inline fromStringI<'a> (s : string) =
let unionCases =
FSharpType.GetUnionCases typeof<'a>
|> Array.filter (fun case ->
case.Name.ToLowerInvariant() = s.ToLowerInvariant()
)
match unionCases with
| [| case |] -> Some(FSharpValue.MakeUnion(case, [||]) :?> 'a)
| _ -> None
let blankStateDecoder : Decoder<BlankState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
let value = fromStringI<BlankState> caseValue
match value with
| Some v -> Decode.succeed v
| None -> sprintf "%s is invalid case for BlankState" caseValue |> Decode.fail)
let requestStateDecoder : Decoder<RequestState> =
Decode.field "case" Decode.string
|> Decode.andThen (fun caseValue ->
match caseValue with
| "BlankMigrationState" ->
Decode.field "fields" (Decode.index 0 blankStateDecoder)
|> Decode.map RequestState.BlankMigrationState
| invalidCase -> sprintf "%s is invalid case for RequestState" caseValue |> Decode.fail)
type Request =
{ ModifiedBy: string
mutable State: RequestState }
let RequestDecoder: Decoder<Request> =
Decode.object(fun fields -> {
ModifiedBy= fields.Required.Field "modifiedBy" Decode.string
State = fields.Required.Field "state" requestStateDecoder
})
let result =
Decode.unsafeFromString RequestDecoder json
JS.console.log result
Thanks for all you support. It was indeed that warning that caused my problem. :( Its working now perfectly fine. Thanks again.
Hi. How would i parse this json?
It is generated from this DU
where Blankstate is a DU of multiple values
I though of starting with that.
But that fails with
What am I doing wrong here?
Thanks, Rico