Я собираюсь начать с конца, а затем вернуться к вашим вопросам.
Решения С классом
Обычно вы делаете тип данных Haskell для каждого из типов JSON и писать FromJSON
классов, которые реализуют синтаксический анализатор. Вам это не обязательно, но оно облегчает психологическую нагрузку и падает в соответствии с тем, что вы можете наблюдать в другом проекте. С этой целью позволяет сделать только пару типов My
для ваших элементов и Mys
для списка этих элементов:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as L8
import qualified Data.Vector as V
sample :: L8.ByteString
sample = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} "
newtype Mys = Mys [My]
deriving (Eq,Ord,Show)
data My = My Int Int
deriving (Eq,Ord,Show)
Хорошо, нет проблем. Теперь мы можем извлечь из вашей k1
записи списка объектов ABC и запустить My
анализатор на эти объекты, чтобы получить только a
и b
значения:
instance FromJSON Mys where
parseJSON (Object v) = do
do k1Val <- v .: "k1"
case k1Val of
Object k1 ->
do k2Val <- k1 .: "k2"
Mys . V.toList <$> mapM parseJSON k2Val
_ -> fail "k1 was not an Object"
parseJSON o = fail $ "Invalid type for Mys: " ++ show o
То есть, чтобы разобрать Mys
нам нужен объект, объект должен иметь запись k1
, являющуюся другим объектом. k1
должен иметь запись k2
, которую мы можем проанализировать как Vector
из My
значений.
instance FromJSON My where
parseJSON (Object v) = My <$> v .: "a" <*> v .: "b"
parseJSON o = fail $ "Invalid type for My: " ++ show o
И данные My
просто синтаксический разбор a
и b
областях, как Int
.Вот:
> decode sample :: Maybe Mys
Just (Mys [My 3 4,My 1 2])
Без класса
Вы спрашивали о :t (fromJust $ (decode sample :: Maybe Object)) .: "k1"
, что это просто причудливый способ просить о типе .:
:
> :t (.:)
(.:) :: FromJSON a => Object -> Text -> Parser a
Таким образом, вы обеспечиваете объекта и Текст, чтобы получить Parser
, как вы сказали. Я бы не советовал снова использовать мону Parser
- вы фактически просто использовали его для decode
. Короче говоря, я бы сказал нет, вы не были на пути к счастью.
Если вы не собираетесь использовать API в соответствии с его конструкцией, просто забудьте о комбинаторах и используйте типы данных напрямую. То есть, много case
уничтожения типов Value
. Сначала k1, который является Object
(всего лишь), затем извлекает значение k2, которое является Array
(a Vector
), наконец, для каждого элемента Vector вы снова извлекаете объект и просматриваете ключи и b
. Я собирался написать это, например, но это очень уродливо, если вы не по крайней мере не позволили себе монаду Maybe
.