Open azuzunaga opened 3 years ago
Does this work via either int string
? either
In this scenario you need to write a "custom" codec - by which I mean one that isn't built entirely out of library-provided primitives, and accordingly you become responsible for ensuring that it round-trips correctly.
All of the library provided stuff that deals with sums uses tagging of some form, as without it there's no way to guarantee that the various branches don't overlap - for instance if you had a codec that specifically decoded the string "value"
, and then another codec that accepts any string value, depending on how you construct the codec this won't reliably do what you want, so the library can't provide anything out of the box that does this safely.
Here's one way you could write this particular codec though:
import Data.Codec as C
secondKeyCodec ∷ CA.JsonCodec SecondKey
secondKeyCodec = C.basicCodec decode encode
where
decode ∷ J.Json → Either CA.JsonDecodeError SecondKey
decode j =
lmap (const (CA.TypeMismatch "SecondKey"))
$ Str <$> CA.decode CA.string j
<|> Int <$> CA.decode CA.int j
encode ∷ SecondKey → J.Json
encode = case _ of
Str s → CA.encode CA.string s
Int i → CA.encode CA.int i
I included type signatures for encode
/ decode
so you can see that there's plenty of ways these functions could be implemented - I used codecs again to encode/decode within them, but you could also just use the raw argonaut core functions like toString
/ fromString
/ toInt
/ fromInt
or whatever you want in there. 🙂
This type of scenario - an untagged value that requires a sum type representation - is basically the worse case for this library, it sucks to deal with, but is a very common real world scenario when you're not in control of the serialisation format. I should perhaps provide an "unsafe" helper that uses the variant setup but that doesn't use tagging and instead tries each of the decoders in turn / encodes without further wrapping. At least then writing these would be a little more in fitting with the rest of the library's usage.
Perhaps a codec could be written for purescript-untagged-union?
I don't really want to add a dependency on that here personally, but yeah, that would be an option. It'd still suffer from the possibility of overlapping values depending on the construction of the type.
I don't think this is actually possible to express, but a safe version would have to have a rule that amounts to "none of the types involved can be coerced to each other".
Thank you both for your help! @garyb, that did the trick.
By the way, I'd be happy to add a PR to the readme with your code example. I think this is a common enough scenario that it might help other people get unstuck. Let me know and either way thanks again!
And if not, I'd accept this as a PR to the cookbook repo. We already have one example of how to use this repo there.
I don't think this is actually possible to express, but a safe version would have to have a rule that amounts to "none of the types involved can be coerced to each other".
Perhaps this kind of thing would be better handled in FFI?
I'm trying to decode a JSON field that can have values of
Int
orString
. Unfortunately I don't have a way to change the serialization. Following the docs it looks like the approach for things like this is creating a sum type and then using variants to decode/encode.However, I've been having trouble getting the right result. I've tried using
variantMatch
andvariantCase
, but both expect a tagged object.Here is a minimally reproducible example using
variantCase
. What is the best approach to handle JSON fields with mixed value types?Thanks!