在Haskell中,如何解码可能是两种不同类型的JSON值?

在Haskell中,如何解码可能是两种不同类型的JSON值?,json,parsing,haskell,aeson,Json,Parsing,Haskell,Aeson,我试图解析一些书目数据,更具体地说,为每个项目提取“主题”字段。数据是json,如下所示: {"rows": [ {"doc":{"sourceResource": {"subject": ["fiction", "horror"]}}}, {"doc":{"sourceResource": {"subject": "fantasy"}}} ]} 如果每个条目都是文本或[文本],我可以拉出“主题”,但我很难同时容纳这两个条目。以下是我的程序的当前状态: {-# LAN

我试图解析一些书目数据,更具体地说,为每个项目提取“主题”字段。数据是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
{-#语言重载字符串}
导入调试跟踪
导入数据。可键入
导入数据.Aeson
导入数据.Text
导入控制
进口管制
将限定数据.ByteString.Lazy作为B导入
导入Network.HTTP.conductor(simpleHttp)
导入符合条件的Data.HashMap.Strict作为HM
导入符合条件的数据。映射为映射
jsonFile::FilePath
jsonFile=“bib.json”
getJSON::IO B.ByteString
getJSON=B.readFile jsonFile
数据文档=文档{行::[Row]}
推导(等式,显示)
数据行=主题列表[文本]
|主题文本
推导(等式,显示)
实例FromJSON文档,其中
parseJSON(对象o)=do
排=
(.:“主体”))
--返回$SubjectText项
返回$SubjectList项
parseJSON=mzero
main::IO()
main=do
d>=
(.:“主体”))
parseJSON=mzero

首先,查看

((o .: "doc") >>=
 (.: "sourceResource") >>=
 (.: "subject")) :: FromJSON b => Parser b
我们可以从中得到任何一个例子。现在,很明显,这可以单独用于
文本
[Text]
,但您的问题是您想要获取
文本
[Text]
。幸运的是,处理这个问题应该相当容易。与其让它为你进一步解码,不如从中获得更多的信息。一旦获得
,就可以将其解码为
文本
,并将其放入
主题文本

SubjectText <$> parseJSON val :: Parser Row
但是,等等,这两种方法中的任何一种都可以,并且它们具有相同的输出类型。请注意,这是的一个实例,让我们确切地说(“任何一个都可以”)。因此,

(SubjectList parseJSON val)(SubjectText parseJSON val)::解析器行

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

我应该注意,每个表达式上的这些类型注释不是必需的,只是为了清晰起见。您应该在实际代码中省略它们。非常感谢,这正是我所需要的,尽管我不得不做了一个修改,即:(SubjectList parseJSON=@reklak:您应该能够省略
=
SubjectText <$> parseJSON val :: Parser Row
SubjectList <$> parseJSON val :: Parser Row
(SubjectList <$> parseJSON val) <|> (SubjectText <$> parseJSON val) :: Parser Row