2014-03-01 2 views
2

일부 서지 데이터를 구문 분석하려고합니다. 구체적으로 각 항목의 '제목'필드를 꺼냅니다. 데이터는 JSON이며,이 같은 같습니다Haskell에서 두 가지 유형이 될 수있는 JSON 값을 어떻게 디코딩하나요?

{"rows": [ 

     {"doc":{"sourceResource": {"subject": ["fiction", "horror"]}}}, 
     {"doc":{"sourceResource": {"subject": "fantasy"}}} 
]} 

모든 항목이 텍스트 또는 [텍스트] 중 하나 인 경우 내가 '주제'를 꺼내 수 있습니다,하지만 난 모두를 수용하는 방법으로 난처한 상황에 빠진거야. 현재 프로그램의 상태는 다음과 같습니다.

{-# LANGUAGE OverloadedStrings#-} 
import Debug.Trace 
import Data.Typeable 
import Data.Aeson 
import Data.Text 
import Control.Applicative 
import Control.Monad 
import qualified Data.ByteString.Lazy as B 
import Network.HTTP.Conduit (simpleHttp) 
import qualified Data.HashMap.Strict as HM 
import qualified Data.Map as Map 

jsonFile :: FilePath 
jsonFile = "bib.json" 

getJSON :: IO B.ByteString 
getJSON = B.readFile jsonFile 


data Document = Document { rows :: [Row]} 
       deriving (Eq, Show) 


data Row = SubjectList [Text] 
     | SubjectText Text 
     deriving (Eq, Show) 


instance FromJSON Document where 
    parseJSON (Object o) = do 
    rows <- parseJSON =<< (o .: "rows") 
    return $ Document rows 
    parseJSON _ = mzero 


instance FromJSON Row where 
    parseJSON (Object o) = do 
    item <- parseJSON =<< ((o .: "doc") >>= 
          (.: "sourceResource") >>= 
          (.: "subject")) 
    -- return $ SubjectText item 
    return $ SubjectList item 
    parseJSON _ = mzero 

main :: IO() 
main = do 
    d <- (decode <$> getJSON) :: IO (Maybe Document) 
    print d 

아무 도움이됩니다.

편집 :

작업 FromJSON 행 인스턴스 :

instance FromJSON Row where 
    parseJSON (Object o) = 
    (SubjectList <$> (parseJSON =<< val)) <|> 
    (SubjectText <$> (parseJSON =<< val)) 
    where 
     val = ((o .: "doc") >>= 
      (.: "sourceResource") >>= 
      (.: "subject")) 
    parseJSON _ = mzero 

답변

2

첫째, 우리는 FromJSON의 인스턴스의 IT 아무것도 나갈 수

((o .: "doc") >>= 
(.: "sourceResource") >>= 
(.: "subject")) :: FromJSON b => Parser b 

의 유형을 확인합니다. 이제 명확하게 Text 또는 [Text]에 대해 개별적으로 문제가 해결 될 수 있지만 문제는 Text또는[Text] 중 하나를 받고 싶다는 것입니다. 다행히도이 문제를 처리하는 것이 매우 쉽습니다. 그것이 당신을 위해 그것을 더 해독하게하는 대신에, 그것의 Value을 얻으십시오. 당신이 Value을 가지고하면, 당신은 Text로 디코딩하고 SubjectText에 넣어 수 :

SubjectText <$> parseJSON val :: Parser Row 

아니면 [Text]로하고 SubjectList에 넣어 :

SubjectList <$> parseJSON val :: Parser Row 

을하지만 잠깐, 이 중 하나가 수행하며 동일한 출력 유형을가집니다. ParserAlternative의 인스턴스이며 정확하게 말합니다 ("어느 쪽이든"할 수 있음). 따라서,

(SubjectList <$> parseJSON val) <|> (SubjectText <$> parseJSON val) :: Parser Row 

Ta-da! 는 (사실, Value로 꺼내 필요하지이었다.. 우리가 대신 ((o .: "doc") >>= (.: "sourceResource") >>= (.: "subject")) 긴 각 부분 식에 체인하지만 그건 추한가 포함 된 수도)

+0

내가주의해야한다 각에서 이러한 유형 약어 표현은 반드시 필요한 것은 아니지만 여기서만 명확하게 나타낼 수 있습니다. 실제 코드에서이를 생략해야합니다. – icktoofay

+0

정말 감사합니다, 이것은 내가 수정을했습니다하지만 내가 필요 정확히 즉 : (SubjectList <$> parseJSON = << val) <|> (SubjectText <$> parseJSON = << 발) – reklak

+0

@reklak : 당신은 생략 할 수 있어야한다 'do' 표기법을 사용하고'let val = ... '대신에'val <- ...'을 쓰면'= <<'이됩니다. – icktoofay