使用 Aeson 读取编码为嵌套字符串的嵌套 JSON 数据
Reading nested JSON data encoded as a nested string with Aeson
我有这个奇怪的 JSON 来解析包含嵌套 JSON ... 的字符串。所以而不是
{\"title\": \"Lord of the rings\", \"author\": {\"666\": \"Tolkien\"}\"}"
我有
{\"title\": \"Lord of the rings\", \"author\": \"{\\"666\\": \\"Tolkien\\"}\"}"
这是我在 FromJSON
的实例中使用 decode
解析嵌套字符串的(失败的)尝试:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Maybe
import GHC.Generics
import Data.Aeson
import qualified Data.Map as M
type Authors = M.Map Int String
data Book = Book
{
title :: String,
author :: Authors
}
deriving (Show, Generic)
decodeAuthors x = fromJust (decode x :: Maybe Authors)
instance FromJSON Book where
parseJSON = withObject "Book" $ \v -> do
t <- v .: "title"
a <- decodeAuthors <?> v .: "author"
return $ Book t a
jsonTest = "{\"title\": \"Lord of the rings\", \"author\": \"{\\"666\\": \\"Tolkien\\"}\"}"
test = decode jsonTest :: Maybe Book
有没有办法一次性解码整个 JSON?谢谢!
这里有几个问题。
首先,你对<?>
的使用是无意义的。我假设这是一个错字,而你的实际意思是 <$>
.
第二个,decodeAuthors
的类型是ByteString -> Authors
,也就是说它的参数是ByteString
类型的,也就是说表达式v .: "author"
必须是 Parser ByteString
类型,这意味着必须有一个实例 FromJSON ByteString
,但这样的实例不存在(出于我目前无法理解的原因)。
你实际上想要的是v .: "author"
到return一个Parser String
(或者可能是Parser Text
),然后让 decodeAuthors
接受 String
并将其转换为 ByteString
(使用 pack
),然后再传递给 decode
:
import Data.ByteString.Lazy.Char8 (pack)
decodeAuthors :: String -> Authors
decodeAuthors x = fromJust (decode (pack x) :: Maybe Authors)
(另请注意:为您提供您认为它们应该具有的声明类型签名是个好主意。这可以让编译器更早地指出错误)
编辑:
正如@DanielWagner 正确指出的那样,pack
可能会混淆 Unicode 文本。如果要正确处理,使用utf8-string
中的Data.ByteString.Lazy.UTF8.fromString
进行转换:
import Data.ByteString.Lazy.UTF8 (fromString)
decodeAuthors :: String -> Authors
decodeAuthors x = fromJust (decode (fromString x) :: Maybe Authors)
但在那种情况下,您还应该注意 jsonTest
的类型:按照您的代码编写方式,其类型将是 ByteString
,但任何非 ASCII 字符都可能是由于 IsString
的工作方式,inside 将被切断。要保留它们,您需要在其上使用相同的 fromString
:
jsonTest = fromString "{\"title\": \"Lord of the rings\", \"author\": \"{\\"666\\": \\"Tolkien\\"}\"}"
我有这个奇怪的 JSON 来解析包含嵌套 JSON ... 的字符串。所以而不是
{\"title\": \"Lord of the rings\", \"author\": {\"666\": \"Tolkien\"}\"}"
我有
{\"title\": \"Lord of the rings\", \"author\": \"{\\"666\\": \\"Tolkien\\"}\"}"
这是我在 FromJSON
的实例中使用 decode
解析嵌套字符串的(失败的)尝试:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Maybe
import GHC.Generics
import Data.Aeson
import qualified Data.Map as M
type Authors = M.Map Int String
data Book = Book
{
title :: String,
author :: Authors
}
deriving (Show, Generic)
decodeAuthors x = fromJust (decode x :: Maybe Authors)
instance FromJSON Book where
parseJSON = withObject "Book" $ \v -> do
t <- v .: "title"
a <- decodeAuthors <?> v .: "author"
return $ Book t a
jsonTest = "{\"title\": \"Lord of the rings\", \"author\": \"{\\"666\\": \\"Tolkien\\"}\"}"
test = decode jsonTest :: Maybe Book
有没有办法一次性解码整个 JSON?谢谢!
这里有几个问题。
首先,你对<?>
的使用是无意义的。我假设这是一个错字,而你的实际意思是 <$>
.
第二个,decodeAuthors
的类型是ByteString -> Authors
,也就是说它的参数是ByteString
类型的,也就是说表达式v .: "author"
必须是 Parser ByteString
类型,这意味着必须有一个实例 FromJSON ByteString
,但这样的实例不存在(出于我目前无法理解的原因)。
你实际上想要的是v .: "author"
到return一个Parser String
(或者可能是Parser Text
),然后让 decodeAuthors
接受 String
并将其转换为 ByteString
(使用 pack
),然后再传递给 decode
:
import Data.ByteString.Lazy.Char8 (pack)
decodeAuthors :: String -> Authors
decodeAuthors x = fromJust (decode (pack x) :: Maybe Authors)
(另请注意:为您提供您认为它们应该具有的声明类型签名是个好主意。这可以让编译器更早地指出错误)
编辑:
正如@DanielWagner 正确指出的那样,pack
可能会混淆 Unicode 文本。如果要正确处理,使用utf8-string
中的Data.ByteString.Lazy.UTF8.fromString
进行转换:
import Data.ByteString.Lazy.UTF8 (fromString)
decodeAuthors :: String -> Authors
decodeAuthors x = fromJust (decode (fromString x) :: Maybe Authors)
但在那种情况下,您还应该注意 jsonTest
的类型:按照您的代码编写方式,其类型将是 ByteString
,但任何非 ASCII 字符都可能是由于 IsString
的工作方式,inside 将被切断。要保留它们,您需要在其上使用相同的 fromString
:
jsonTest = fromString "{\"title\": \"Lord of the rings\", \"author\": \"{\\"666\\": \\"Tolkien\\"}\"}"