我已经在 Aeson 解码问题上苦苦挣扎了一段时间了。简而言之,当在应用程序的上下文中使用时,如下面第 (6) 行所示,解码器失败,给出错误
src/CFUpload.hs:(66,6)-(71,27): Non-exhaustive patterns in function parseJSON
我已在解码器 instance FromJSON CFUploadResponse
中指出了下面的这些行。
然而,当解码器应用于 repl 中的数据时,我认为它是
接收,成功(见标记(***)的讨论)。
现在是血淋淋的细节:首先是抛出错误的代码, 然后是类型,然后是解码器和讨论。
代码。
post "/image" $ do
image <- jsonData :: ActionM CFImage.CFImage -- (1)
liftIO $ CFImage.downloadImage image -- (2)
cfImageUploadUrl <- liftIO Image.requestCFToken -- (3)
let filename = CFImage.getFilenameFromImage image -- (4)
cfUploadedImageResponse <- liftIO $ Image.uploadTheImage cfImageUploadUrl filename -- (5)
let cfUploadedImageResponse' = Data.Aeson.decode $ BL.pack cfUploadedImageResponse :: Maybe CFUpload.CFUploadResponse -- (6)
text $ pack $ show cfUploadedImageResponse' -- (7)
这是 Scotty 服务器应用程序的一部分。我正在使用 Postman 测试代码。第 (5) 行一切正常:服务器接受 POSTed 数据,其中包含图像 URL 和图像文件名。第 (2) 行使用该数据将图像下载到目录 cf-image
中的文件中。 。第 (3) 行向 Cloudflare 请求一次性图像上传 URL。第 (4) 行提取文件名,并在第 (5) 行使用该文件名将图像 POST 到 cloudflare,返回包含指向 Cloudflare 服务器上图像的 URL 的数据。我知道这个请求成功了,因为我已经短路了上面的代码,将第 (6) 和 (7) 行替换为
text $ pack $ cfUploadedImageResponse
响应是
"{\n \"result\": {\n \"id\": \"673996fb-4d26-4332-6e6b-e8bf7b608500\",\n \"filename\": \"bird2.jpg\",\n \"uploaded\": \"2023-03-18T22:53:56.705Z\",\n \"requireSignedURLs\": false,\n \"variants\": [\n \"https://imagedelivery.net/9U-0Y4sEzXlO6BXzTnQnYQ/673996fb-4d26-4332-6e6b-e8bf7b608500/public\"\n ]\n },\n \"success\": true,\n \"errors\": [],\n \"messages\": []\n}"
将此字符串称为 testResponse
。如果喂testResponse
对于Python的打印,你会得到
{
"result": {
"id": "673996fb-4d26-4332-6e6b-e8bf7b608500",
"filename": "bird2.jpg",
"uploaded": "2023-03-18T22:53:56.705Z",
"requireSignedURLs": false,
"variants": [
"https://imagedelivery.net/9U-0Y4sEzXlO6BXzTnQnYQ/673996fb-4d26-4332-6e6b-e8bf7b608500/public"
]
},
"success": true,
"errors": [],
"messages": []
}
类型。
这个字符串,我们称之为 testResponsePretty
,据我所知,是正确的:与以下类型进行比较:
data CFUploadResponse = CFUploadResponse
{
result :: CFUploadResult,
success :: Bool,
errors :: [String],
messages :: [String]
} deriving Show
data CFUploadResult = CFUploadResult {
id :: String,
filename :: String,
uploaded :: String,
requireSignedURLs :: Bool,
variants :: [String]
} deriving Show
解码器。
这是解码器:
instance FromJSON CFUploadResponse where
parseJSON (Object v) = -- (66)
CFUploadResponse <$> -- (67)
v .: "result" <*> -- (68)
v .: "success" <*> -- (69)
v .: "errors" <*> -- (70)
v .: "messages" -- (71)
-- Tell Aeson how to convert a CFUploadResponse object to a JSON string.
instance FromJSON CFUploadResult where
parseJSON = withObject "CFUploadResult" $ \o -> do
id <- o .: Data.Text.pack "id"
filename <- o .: Data.Text.pack "filename"
uploaded <- o .: Data.Text.pack "uploaded"
requireSignedURLs <- o .: Data.Text.pack "requireSignedURLs"
variants <- o .: Data.Text.pack "variants"
return (CFUploadResult id filename uploaded requireSignedURLs variants)
讨论。 (***)
尤其令人困惑的是以下内容。让testResponse
如上并令
myDecode str = Data.Aeson.eitherDecode $ BL.pack str :: Either String (Maybe CFUpload.CFUploadResponse)
然后这样做:
$ stack repl
ghci> myDecode testResponse
结果是
Right (Just (CFUploadResponse {result = CFUploadResult {id = "49660d63-a43f-4011-1a7a-ff6435305d00", filename = "bird2.jpg", uploaded = "2023-03-16T23:08:22.768Z", requireSignedURLs = False, variants = ["https://imagedelivery.net/9U-0Y4sEzXlO6BXzTnQnYQ/49660d63-a43f-4011-1a7a-ff6435305d00/public"]}, success = True, errors = [], messages = []}))
最佳答案
当您将第 (6) 行和第 (7) 行替换为 text $ pack $cfUploadedImageResponse
时,来自 Web 服务器的响应字节流应为:
{
"result":...
...
}
不是:
"{\n \"result\":...
也就是说,响应应该以左大括号开头,而不是双引号。
我猜测您的 uploadTheImage
函数返回的是 show response
的结果,而不是响应字符串本身。如果 cfUploadedImageResponse 的实际值是一个字符串,其第一个字符是双引号,那么您要求 Aeson 解码恰好是单个字符串的 JSON 值(其内容是一些 JSON,但这与解码器无关)。这将导致您的实例中的模式匹配:
parseJSON (Object v)
失败,因为v::Values
是一个String
而不是Object
。
关于haskell - 尝试使用 Aeson 解码时出现莫名其妙的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75779314/