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 for Text
and [a]
. Because of this you can use the Alternative
operator to write your instance like this:
instance FromJSON Foo where
parseJSON v = (SimpleText <$> parseJSON v)
<|> (ListOfText <$> parseJSON v)
<|> (ListOfListOfText <$> parseJSON v)
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 a ListOfListOfText
? Since we cant pattern match on a Vector
directly, we can turn it into a list and pattern match on it:
instance FromJSON Foo where
parseJSON v = case v of
-- If its a string, we return the string as a SimpleText
(String s) -> return $ SimpleText s
-- If its an array, we turn the vector to a list so we can pattern match on it
(Array a) -> case V.toList a of
-- If its a empty list, we return a empty ListOfText
[] -> return $ ListOfText []
-- If the first value is a string, we put it as the first element of our ListOfTexts and try to parse the rest.
(String s: xs) -> ListOfText . (s:) <$> mapM parseJSON xs
-- If the first value is an array, we try to parse it as [Text], then parse the rest.
(Array a: xa) -> ListOfListOfText <$> ((:) <$> parseJSON (Array a) <*> mapM parseJSON xa)
-- If the first value is neither a string or array we return a error message.
_ -> fail "Expected an Array or an Array of Arrays."
-- If the top level value is not a string or array we return a error message.
_ -> fail "Expected a String or Array"