2017-02-03 18 views
0

Cosiderて以下JSON構造:解析JSONアイソーン

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

とHaskellのデータ型の2つのInt一方、[My]:上記JSONマイのリストに解析されるべきである

data My = My Int Int 

それぞれはJSON配列の "a"と "b"キーから取得する必要があります:

[My 3 4, My 1 2] 

確かに私はalreですそれの最も単純な部分で厄介な問題に直面している。

は、ここで私はアイソーンを使用して開始方法は次のとおりです。REPLで

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」のオブジェクトを取得し、動作しません:私はここにParser aタイプを受信して​​い

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

、私はこの時点で別のObjectまたはMaybe Objectを取得期待/必要があると思います。

私は正しい経路にいますか?

答えて

0

私は最後から始めて、あなたの質問に戻るつもりです。

通常、あなたのJSONの種類ごとに、Haskellのデータ型を作成し、パーサを実装FromJSONクラスを記述クラス

で解きます。あなたはそうする必要はありませんが、精神的な負荷を軽くし、他のプロジェクトで観察するかもしれないインラインで落ちます。

{-# 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) 

[OK]を、無問題:そのために、これらの要素のリストについては、あなたの要素とMysのためだけのカップルタイプMyを行うことができます。今、私たちはあなたの​​レコードからABCオブジェクトのリストを抽出し、ちょうどab値を取得するために、これらのオブジェクトにMyパーサを実行することができます。私たちはオブジェクトが必要Mysを解析することです

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 

を、オブジェクトに別のオブジェクトである​​エントリが必要です。​​は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データはIntとしてabフィールドの単なる構文解析です。見よ:クラス

なし

> 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の破壊がたくさんあります。まず、Objectのk1(ちょうどHashMap)で、ArrayVector)のk2値を抽出します。最後に、Vectorの各要素について、abのキーを再度検索します。私はそれを書き上げようとしていましたが、少なくともあなた自身がMaybeモナドを許さないならば、非常に醜いです。

0

チュートリアルが役に立ちました。

それに続いて、私は次のコードに到着しました。 sampleを拡張して、さまざまなサンプル(さまざまな欠陥のあるもの)の処理を検査できるようにします。 mainは、処理されるサンプルのIDが、コンパイルされた実行可能ファイルの最初の引数として指定されることを想定しています。

JSON構造の一番下から上に向かって、関数parseMy :: Value -> Parser Myはオブジェクトを 'a' 'b'キーで処理してMy(成功した場合)を返します。中間ヘルパー関数parseMyList'は、そのようなオブジェクトの配列を処理してMyのリストを生成する。 parseMyListはキー 'k1'を持つオブジェクトを処理し、キー 'k2'を持つオブジェクトを順番に返して、Myのリストを生成します。

mainの場合、parseは、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"