2017-02-03 9 views
0

Cosider следующая структура JSON:Разбор JSON с эсон

{"k1": 
    {"k2": 
    [{"a": 3, "b": 4, "c": 2}, 
    {"a": 1, "b": 2, "c": 9}]}, 
"irrelevant": "x"} 

и Haskell типа данных:

data My = My Int Int 

выше JSON должен быть проанализирован в Список моих: [My], в то время как два Int должны каждый быть взяты из «а» и клавиша «Ъ» из массива JSON:

[My 3 4, My 1 2] 

Правда я нахожусь ALRE ады сталкиваются с проблемами с простейшими ее частями.

Вот как я начал использовать эсон:

import   Data.Aeson 
import qualified Data.ByteString.Lazy.Char8 as L8 

sample :: L8.ByteString 
sample = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} " 

В РЕПЛ:

decode sample :: Maybe Object 
Just (Object (fromList [("irreleva... 

Это работает, как и ожидалось, JSON разобран. Однако следующий шаг, получить объект в ключе «k1», не работает:

:t (fromJust $ (decode sample :: Maybe Object)) .: "k1" 
... 
    :: FromJSON a => aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser a 

Я получения Parser a типа здесь, я должен был бы/ожидать получения другой Object или Maybe Object в этой точке.

Есть ли я на правильном пути?

ответ

0

Я собираюсь начать с конца, а затем вернуться к вашим вопросам.

Решения С классом

Обычно вы делаете тип данных 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.

0

Урок может помочь вам, поскольку это помогло мне.

После этого я пришел к следующему коду. Он расширяет sample, чтобы можно было обрабатывать различные образцы (некоторые с различными дефектами). main ожидает, что идентификатор образца, который должен быть обработан, будет предоставлен в качестве первого аргумента скомпилированного исполняемого файла.

Начиная с нижней части структуры JSON и работая вверх, функция parseMy :: Value -> Parser My обрабатывает объект с помощью клавиш 'a' 'b', чтобы получить My (при успешном выполнении); промежуточная вспомогательная функция parseMyList' обрабатывает массив таких объектов, чтобы получить список My; и parseMyList обрабатывает объект с ключом «k1», уступая, в свою очередь, объекту с ключом «k2», чтобы также получить список My.

В main, parse применяется parseMyList :: Value -> Parser [My] к результату decode (если она была успешной).

{-# LANGUAGE OverloadedStrings #-} 

module Main (main) where 

import   Data.Aeson ((.:), decode, Value) 
import   Data.Aeson.Types (parse, Parser, withArray, withObject) 
import qualified Data.ByteString.Lazy.Char8 as L8 (ByteString) 
import qualified Data.Vector as V (toList) 
import   System.Environment (getArgs) 

data My = My Int Int deriving (Show) 

sample :: String -> L8.ByteString 
sample "1" = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} " 
sample "2" = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"c\": 9}]}, \"irrelevant\": \"x\"} " 
sample "3" = "{\"k1\":{\"k3\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} " 
sample "4" = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}]}, \"irrelevant\": \"x\"} " 
sample _ = "Error" 

parseMy :: Value -> Parser My 
parseMy = withObject "object" $ \o -> do 
    a <- o .: "a" 
    b <- o .: "b" 
    return $ My a b 

parseMyList' :: Value -> Parser [My] 
parseMyList' = withArray "array" $ \arr -> 
    mapM parseMy (V.toList arr) 

parseMyList :: Value -> Parser [My] 
parseMyList = withObject "object" $ \o -> do 
    k1 <- o .: "k1" 
    k2 <- k1 .: "k2" 
    parseMyList' k2 

main :: IO() 
main = do 
    args <- getArgs 
    case args of 
     [] -> fail "expected sample identity as the first argument" 
     n:_ -> do 
      putStrLn $ "Processing sample " ++ n 
      case decode (sample n) of 
       Just result -> print $ parse parseMyList result 
       Nothing  -> fail "decoding failed"