1

我有以下函数可以使用该Data.Aeson库解码 JSON 文件:

data SearchResult = SearchResult {
  items :: [Item]
} deriving (Show)

instance FromJSON SearchResult where
  parseJSON :: Value -> Parser SearchResult
  parseJSON (Object v) = SearchResult <$>
    parseJSON (fromJust $ HM.lookup "items" v)
  parseJSON _ = mzero

data Item = Item {
  volumeInfo :: VolumeInfo
} deriving (Show)

instance FromJSON Item where
  parseJSON :: Value -> Parser Item
  parseJSON (Object v) = Item <$>
    parseJSON (fromJust $ HM.lookup "volumeInfo" v)
  parseJSON _ = mzero

data VolumeInfo = VolumeInfo {
  title :: String,
  authors :: [String],
  publisher :: String,
  publishedDate :: String,
  industryIdentifiers :: [IndustryIdentifier],
  pageCount :: Int,
  categories :: [String]
} deriving (Show)

instance FromJSON VolumeInfo where
  parseJSON :: Value -> Parser VolumeInfo
  parseJSON (Object v) = VolumeInfo <$>
    v .: "title" <*>
    v .: "authors" <*>
    v .: "publisher" <*>
    v .: "publishedDate" <*>
    parseJSON (fromJust $ HM.lookup "industryIdentifiers" v) <*>
    v .: "pageCount" <*>
    v .: "categories"
  parseJSON _ = mzero

data IndustryIdentifier = IndustryIdentifier {
  identifierType :: String,
  identifier :: String
} deriving (Show)

instance FromJSON IndustryIdentifier where
  parseJSON :: Value -> Parser IndustryIdentifier
  parseJSON (Object v) = IndustryIdentifier <$>
    v .: "type" <*>
    v .: "identifier"
  parseJSON _ = mzero

而这个功能:

getBook content = do
  putStrLn (Data.ByteString.Lazy.Char8.unpack content)
  let searchResult = decode content :: Maybe SearchResult
  print (isNothing searchResult)
  print searchResult

该函数getBook适用于许多 JSON 文件。这是一个例子:

False
Just (SearchResult {items = [Item {volumeInfo = VolumeInfo {title = "A Memoir of Jane Austen", authors = ["James Edward Austen-Leigh","Jane Austen, James Austen-Leigh"], publisher = "Wordsworth Editions", publishedDate = "2007", industryIdentifiers = [IndustryIdentifier {identifierType = "ISBN_10", identifier = "1840225602"},IndustryIdentifier {identifierType = "ISBN_13", identifier = "9781840225600"}], pageCount = 256, categories = ["Novelists, English"]}}]})

JSON 内容已成功解码,因此在第一行isNothing返回,然后是解码后的内容。如果我将此 JSON 文件作为False再次运行该函数,我将得到以下输出:content

True
Nothing

该文件无法解码(因为categoriesJSON 文件中没有字段),因此isNothing返回True,并Nothing打印在屏幕上。现在的问题是当我将此 JSON 文件作为content. 我明白了:

*** Exception: Maybe.fromJust: Nothing

执行时抛出异常print (isNothing searchResult),我不明白为什么True不像前面的例子那样返回(因为在这种情况下没有 field industryIdentifiers,例如)。我错过了什么或做错了什么?

编辑:

我发现每次 JSON 文件不包含该字段时都会出现问题industryIdentifiers。它在这一行失败:

parseJSON (fromJust $ HM.lookup "industryIdentifiers" v) <*>
4

1 回答 1

2

github 包定义了一个方便的操作符来定义数组字段:

-- | A slightly more generic version of Aeson's @(.:?)@, using `mzero' instead
-- of `Nothing'.
(.:<) :: (FromJSON a) => Object -> T.Text -> Parser [a]
obj .:< key = case Map.lookup key obj of
                   Nothing -> pure mzero
                   Just v  -> parseJSON v

(这里Map是 的别名Data.HashMap.Lazy

然后FromJSONVolumeInfo 的实例将被定义如下:

instance FromJSON VolumeInfo
  v .: "title" <*>
  ...
  v .:< "industryIdentifiers" <*>
  ...
于 2015-01-03T01:43:14.197 回答