2

我正在尝试解析一些书目数据,更具体地说,为每个项目提取“主题”字段。数据是 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 Row 实例:

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

1 回答 1

2

首先,看类型

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

我们可以摆脱它的任何实例FromJSON。现在,很明显,这可以单独使用Text[Text]单独使用,但您的问题是您想要获得Text or [Text]。幸运的是,处理这个问题应该相当容易。与其让它为您进一步解码,不如从中Value解脱出来。一旦你有了 a Value,你可以将它解码为 aText并将其放入 a SubjectText

SubjectText <$> parseJSON val :: Parser Row

或作为 a[Text]并将其放在 a 中SubjectList

SubjectList <$> parseJSON val :: Parser Row

但是等等,其中任何一个都可以,并且它们具有相同的输出类型。请注意,这Parser是 的一个实例Alternative,它可以让我们准确地说(“任何一个都会做”)。因此,

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

达达!(实际上,没有必要将它作为 拉出Value;我们可以改为将长((o .: "doc") >>= (.: "sourceResource") >>= (.: "subject"))链嵌入到每个子表达式中。但这很难看。)

于 2014-03-01T22:59:36.277 回答