Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upgetFieldOptional possibly broken in nested structures? #32
Comments
This comment has been minimized.
This comment has been minimized.
|
I think what's going on here is If you want to treat the null value the same as the case where the key is missing, I would define a new function that treats the null case the same as the case where the key is missing. foreign import isNull :: forall a. a -> Boolean
getFieldOptional' :: forall a. DecodeJson a => JObject -> String -> Either String (Maybe a)
getFieldOptional' o s = (case _ of
Just v -> if isNull v then Nothing else v
Nothing -> Nothing
) <$> (getFieldOptional o s)Where exports.isNull = function(v) {
return v === null;
} |
This comment has been minimized.
This comment has been minimized.
vagifverdi
commented
May 28, 2017
|
The law of the least surprise dictates that nulls propagate. While I'm sure a function that recognizes the distinction between null and absent keys is useful and needed, I do not think it should replace the most anticipated outcome. I do not know whether fixing getFieldOptional would break too much of existing code. And if that's the case perhaps it would be possible to create a new similar function with propagating nulls? Or make getFieldOptional to have the most expected behavior, and move current functionality in a different function. |
This comment has been minimized.
This comment has been minimized.
|
I ran into this same error as well. I was under the impression that argonaut's I wrote a small test program to show the difference between Aeson's #!/usr/bin/env stack
-- stack --resolver lts-8.14 script --package aeson
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
(FromJSON(parseJSON), Value, (.:), (.:?), (.:!), eitherDecode,
withObject)
import Data.Aeson.Types (Parser)
data Foo = Foo
{ fooInt :: Int
, fooBar :: Maybe String
, fooBaz :: Maybe String
} deriving Show
instance FromJSON Foo where
parseJSON :: Value -> Parser Foo
parseJSON = withObject "Foo" $ \o ->
Foo
<$> o .: "int"
<*> o .:? "bar"
<*> o .:! "baz"
main :: IO ()
main = do
print myEitherFooNoBarBaz
print myEitherFooNullBarBaz
myEitherFooNoBarBaz :: Either String Foo
myEitherFooNoBarBaz = eitherDecode "{ \"int\": 1 }"
myEitherFooNullBarBaz :: Either String Foo
myEitherFooNullBarBaz =
eitherDecode "{ \"int\": 1, \"bar\": null, \"baz\": null }"Here is the output of running this:
You can see that the I assumed that argonaut's I agree with @vagifverdi that argonaut's I do feel it is somewhat unfortunate that the suffix character |
This comment has been minimized.
This comment has been minimized.
|
I don't really have a strong preference about how this behaves, but due to the current poor encode/decode instances for I don't really think that's something people are likely depending on, but you never know. Just thought I'd point it out as another consideration if we do change the behaviour of the operator. |
This comment has been minimized.
This comment has been minimized.
mengu
commented
Sep 16, 2018
|
i'm definitely voting for the change. the current behavior is not intuitive. |
thomashoneyman
self-assigned this
Nov 11, 2018
This comment has been minimized.
This comment has been minimized.
|
@mengu @garyb @cdepillabout @vagifverdi @dgendill I agree that this should be improved, and I would like to take action on this and merge an update as soon as I have some idea of consensus from those of you involved so far. If there's no set opinion, then I'll most likely go with adding a new operator as in #32. I don't want to linger too long -- it's been long enough already -- so I will most likely make an update in 1 week unless there are objections. I'm not sure whether it would be better for The main questions for me comes down to whether there's ever a case in which you'd want the current behavior of My current preference is probably to remove the As I know this has been an issue for @foresttoney @crcornwell and @davezuch in the past, I'd appreciate hearing your suggestions as well. |
thomashoneyman
added
help wanted
question
labels
Nov 11, 2018
This comment has been minimized.
This comment has been minimized.
|
I wouldn't assume there won't ever be a use case for the current behavior, I've encountered codebases and API's where There's the problem of what to name each combinator though. Following Aeson's naming convention, we would update Perhaps the solution is to break the API completely and switch to Aeson's naming for these combinators, renaming |
This comment has been minimized.
This comment has been minimized.
|
I'd be OK with switching to the Aeson naming scheme given how similar these interfaces already are and that the slight differences have caused confusion in the past. From what I can tell, these operators are more influenced by Aeson than by Scala's Argonaut. |
davezuch
referenced this issue
Nov 15, 2018
Merged
Add expected behavior for null values to decode functions #47
This comment has been minimized.
This comment has been minimized.
mengu
commented
Nov 23, 2018
|
thank you very much for the work @thomashoneyman. |
This comment has been minimized.
This comment has been minimized.
|
Closed by #47 |
vagifverdi commentedMay 20, 2017
•
edited
Here's the minimal example to reproduce:
When each Baz in the list has Int value in bazOpt then "bad" works fine.
When some Bazes in the list has null in bazOpt field then "bad" give this error:
Couldn't decode List Value is not a Number.
"good" works fine even with nulls.
is getFieldOptional broken?