2017-02-03 2 views
0

Cosider하여 다음 JSON 구조 :파싱 JSON 아이손

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

하스켈 데이터 유형 : 두 지능 반면 [My] : 위 JSON 내 목록으로 해석되어야

data My = My Int Int 

각각은 JSON 배열의 "a"와 "b"키로부터 가져와야합니다 :

[My 3 4, My 1 2] 

틀림없이 나는 ady는 그것의 가장 간단한 부분으로 문제를 직면합니다.

는 여기에 내가 아이손을 사용하기 시작하는 방법은 다음 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 유형 각각에 대한 하스켈 데이터 형식을하고 파서를 구현 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) 

가 좋아 :이를 위해 이들 요소의 목록은 단지 몇 종류 당신의 요소에 대한 MyMys을 할 수 있습니다. 이제 우리는 당신의 k1 기록에서 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 

을, 오브젝트에는 다른 오브젝트 인 k1 항목이 있어야합니다. k1VectorMy 값인 것으로 해석 할 수있는 k2 항목이 있어야합니다.

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)을 입력하고 Array (Vector) 인 k2 값을 추출한 다음 벡터의 각 요소에 대해 다시 오브젝트를 추출하고 ab 키를 찾습니다. 나는 그것을 예를 들어 쓸 것입니다. 그러나 적어도 당신 자신이 Maybe 모나드를 허용하지 않는다면 그것은 매우 추합니다.

0

자습서 이 도움이 될 수 있습니다.

다음 코드에 도달했습니다. sample을 확장하여 다양한 샘플 (일부는 다양한 결함이 있음)의 처리를 검사 할 수 있습니다. main은 처리 할 샘플의 ID가 컴파일 된 실행 파일의 첫 번째 인수로 제공 될 것으로 기대합니다.

JSON 구조의 맨 아래에서 시작하여 위쪽으로 작업 할 경우 parseMy :: Value -> Parser My은 'a' 'b'키를 사용하여 객체를 처리하여 My (성공하는 경우)을 산출합니다. 중간 도우미 함수 parseMyList'은 이러한 개체의 배열을 처리하여 My의 목록을 산출합니다. parseMyList은 키 'k1'을 사용하여 객체를 처리하고 키 'k2'를 갖는 객체를 차례로 생성하여 My의 목록을 생성합니다.

main에서 parsedecode의 결과에 을 적용합니다 (성공한 경우).

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