2014-03-01 13 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

首先,看看

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

類型,我們可以擺脫任何東西這就是FromJSON一個實例。現在,很明顯,這可以單獨適用於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 = << VAL) – reklak

+0

@reklak:你應該能夠省略'= <<'如果你使用'do'符號並且在它之上有'val < - ...'而不是'let val = ...'。 – icktoofay

相關問題