I am using Data.Aeson to parse JSON to my custom type.
I try to pattern match Vector Value
(Array
) in my FromJSON
instance, but don't know how I can do it. JSON value
key can have a value of a String
, a list of String
or a list of list of String
.
instance FromJSON Foo where
parseJSON (Object o) =
case lookup "value" o of
Just (String s) -> pure $ SimpleText s
Just foo@(Array (String s)) -> pure $ ListOfText $ V.toList <$> V.mapM (parseJSON :: Value -> Parser Text) foo
Just foo@(Array (Array (String s))) -> pure $ ListOfListOfText $ V.toList <$> V.mapM (parseJSON :: Value -> Parser Text) $ V.toList <$> V.mapM (parseJSON :: Value -> [Parser Value]) foo
with
data Foo = SimpleText Text
| ListOfText [Text]
| ListOfListOfText [[Text]]
deriving (Show, Eq, Ord)
Can I use pattern matching on Array to handle this case? or should I manually check the type of every Value
? and how do to it?
No, you cannot pattern match the way you are trying to do here. A JSON Array can contain values of different types, and you cannot pattern match on all values in a list like they where one.
There are several ways to solve your actual problem here. There is a easy way, and there is an explicit way that will give you better error messages.
Easy way
The easy way is to use the fact that there already exist
FromJSON
instances forText
and[a]
. Because of this you can use theAlternative
operator to write your instance like this:The trick here is that Aeson first will try to parse a
Text
value, then if it fails it will try a[Text]
, if it fails again it will try[[Text]]
.The problem with this solution is that if your JSON is malformed the error messages might not make sense. For example if you give it a top level Null value your error will be that it expects a
[[Text]]
, since you will always get the error for the last value in the chain.Explicit way
To get better error messages you have to be more expicit about what values you expect. What if the result is an empty array, should it be a
ListOfText
or aListOfListOfText
? Since we cant pattern match on aVector
directly, we can turn it into a list and pattern match on it: