如何使用Aeson解析位响应JSON?
我一直在绞尽脑汁试图用Aeson来解析那些刻薄的回答。 有人能给我一个关于Haskell类型应该定义的提示吗 以及如何使用Aeson将以下内容解析为这些类型:如何使用Aeson解析位响应JSON?,json,haskell,aeson,Json,Haskell,Aeson,我一直在绞尽脑汁试图用Aeson来解析那些刻薄的回答。 有人能给我一个关于Haskell类型应该定义的提示吗 以及如何使用Aeson将以下内容解析为这些类型: // BITLY EXPAND RESPONSE { "data": { "expand": [ { "global_hash": "900913", "long_url": "http://google.com/", "short_url": "http://bit
// BITLY EXPAND RESPONSE
{
"data": {
"expand": [
{
"global_hash": "900913",
"long_url": "http://google.com/",
"short_url": "http://bit.ly/ze6poY",
"user_hash": "ze6poY"
}
]
},
"status_code": 200,
"status_txt": "OK"
}
// BITLY SHORTEN RESPONSE
{
"data": {
"global_hash": "900913",
"hash": "ze6poY",
"long_url": "http://google.com/",
"new_hash": 0,
"url": "http://bit.ly/ze6poY"
},
"status_code": 200,
"status_txt": "OK"
}
以下是我迄今为止所尝试的:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module BitlyClientResponses where
import Control.Applicative
import Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as L (pack)
import qualified Data.HashMap.Strict as M
data DataStatusCodeStatusTxt =
DSCST { ddata :: ResponseData
, status_code :: Integer
, status_txt :: String
}
deriving (Eq, Show)
data ResponseData
= ExpandResponseData { expand :: [Response]
}
deriving (Eq, Show)
data Response = ExpandResponse { long_url :: String -- URI
, global_hash :: String
, short_url :: String -- URI
, user_hash :: String
-- , hash :: [String]
-- , error :: String
}
| J String
| N String
deriving (Eq, Show)
instance FromJSON DataStatusCodeStatusTxt where
parseJSON (Object o) = DSCST <$>
o .: "data" <*>
o .: "status_code" <*>
o .: "status_txt"
parseJSON x = fail $ "FAIL: DataStatusCodeStatusTxt: " ++ (show x)
instance FromJSON ResponseData where
parseJSON (Object o) =
case M.lookup "expand" o of
-- LOST RIGHT HERE
Just v -> return $ ExpandResponseData [J ((show o) ++ " $$$ " ++ (show v))]
Nothing -> return $ ExpandResponseData [N "N"]
parseJSON x = fail $ "FAIL: ResponseData: " ++ (show x)
instance FromJSON Response where
parseJSON (Object o) = ExpandResponse <$>
o .: "long_url" <*>
o .: "global_hash" <*>
o .: "short_url" <*>
o .: "user_hash"
-- o .: "hash" <*>
-- o .: "error" <*>
parseJSON x = fail $ "FAIL: Response: " ++ (show x)
parseResponse :: String -> Either String DataStatusCodeStatusTxt
parseResponse x = eitherDecode $ L.pack x
我回来了(也手工编辑):
在代码中,查找--LOST RIGHT HERE
。我不知道如何解析“expand”
的数组
如果能看到如何取得进展,那就太好了。也许我走错了路,有人可以纠正我的错误(例如,也许到目前为止我定义的数据类型不正确)。有效使用
Aeson
的诀窍是递归调用parseJSON
。这是在使用(.:)
运算符时隐式完成的,因此看到类似M.lookup
的内容通常是一个不好的迹象。我将提供一个简化的示例:一个(纬度、经度)对的路径,由JSON对象的JSON数组表示
data Path = Path { points :: [Point] }
data Point = Point { lat :: Double, lon :: Double }
-- JSON format looks a bit like this
--
-- { "points": [ {"latitude": 86, "longitude": 23} ,
-- {"latitude": 0, "longitude": 16} ,
-- {"latitude": 43, "longitude": 87} ] }
instance FromJSON Path where
parseJSON = withObject "path" $ \o ->
Path <$> o .: "points"
instance FromJSON Point where
parseJSON = withObject "point" $ \o ->
Point <$> o .: "latitude"
<*> o .: "longitude"
这说明我需要查看名为“points”
的条目,并尝试将其解析为构建路径所需的任何类型,在本例中,是点的列表,[Point]
。这种使用依赖于递归定义的FromJSON
实例。我们需要解析一个数组,但幸运的是已经存在FromJSON
实例
instance FromJSON a => FromJSON [a] where ...
instance FromJSON Obj where
parseJSON v = okParse v <|> elseParse v
它被解释为JSON类型的JSON数组a
可以解析为什么。在我们的例子中,a~点
,所以我们只定义该实例
instance FromJSON Point where ...
然后递归地依赖于
instance FromJSON Double where ...
这是相当标准的
您可以使用的另一个重要技巧是使用()
连接多个解析。我将把响应
数据类型简化一点,它要么解析为特定的对象
,要么失败,并生成一个简单的、动态键入的值
,作为默认值。首先,我们将独立编写每个解析器
data Obj = Obj { foo :: String, bar :: String }
| Dyn Value
okParse :: Value -> Parser Obj
okParse = withObject "obj" (\o -> Obj <$> o .: "foo" <*> o .: "bar")
elseParse :: Value -> Parser Obj
elseParse v = pure (Dyn v)
在这种情况下,aeson
将首先尝试使用okParse
,如果失败,则使用elseParse
。由于elseParse
只是一个纯值,因此它永远不会失败,因此提供“默认”回退。有效使用Aeson
的诀窍是递归调用parseJSON
。这是在使用(.:)
运算符时隐式完成的,因此看到类似M.lookup
的内容通常是一个不好的迹象。我将提供一个简化的示例:一个(纬度、经度)对的路径,由JSON对象的JSON数组表示
data Path = Path { points :: [Point] }
data Point = Point { lat :: Double, lon :: Double }
-- JSON format looks a bit like this
--
-- { "points": [ {"latitude": 86, "longitude": 23} ,
-- {"latitude": 0, "longitude": 16} ,
-- {"latitude": 43, "longitude": 87} ] }
instance FromJSON Path where
parseJSON = withObject "path" $ \o ->
Path <$> o .: "points"
instance FromJSON Point where
parseJSON = withObject "point" $ \o ->
Point <$> o .: "latitude"
<*> o .: "longitude"
这说明我需要查看名为“points”
的条目,并尝试将其解析为构建路径所需的任何类型,在本例中,是点的列表,[Point]
。这种使用依赖于递归定义的FromJSON
实例。我们需要解析一个数组,但幸运的是已经存在FromJSON
实例
instance FromJSON a => FromJSON [a] where ...
instance FromJSON Obj where
parseJSON v = okParse v <|> elseParse v
它被解释为JSON类型的JSON数组a
可以解析为什么。在我们的例子中,a~点
,所以我们只定义该实例
instance FromJSON Point where ...
然后递归地依赖于
instance FromJSON Double where ...
这是相当标准的
您可以使用的另一个重要技巧是使用()
连接多个解析。我将把响应
数据类型简化一点,它要么解析为特定的对象
,要么失败,并生成一个简单的、动态键入的值
,作为默认值。首先,我们将独立编写每个解析器
data Obj = Obj { foo :: String, bar :: String }
| Dyn Value
okParse :: Value -> Parser Obj
okParse = withObject "obj" (\o -> Obj <$> o .: "foo" <*> o .: "bar")
elseParse :: Value -> Parser Obj
elseParse v = pure (Dyn v)
在这种情况下,aeson
将首先尝试使用okParse
,如果失败,则使用elseParse
。既然elseParse
只是一个纯值,它永远不会失败,因此提供了一个“默认”回退。为什么您将长url和短url定义为[String]而不是纯字符串?感谢您捕捉到这一点。我复制了ExpandRequest构造函数,您可以在其中提供多个URL。但是,正如您所指出的,响应应该是String
——很好!我更新了类型。也许这就是我的问题的一部分(现在将尝试)-还需要研究@abrahamson响应。为什么您将long_url和short_url定义为[String]而不是纯字符串?谢谢您的关注。我复制了ExpandRequest构造函数,您可以在其中提供多个URL。但是,正如您所指出的,响应应该是String
——很好!我更新了类型。也许这是我的问题的一部分(我现在会试试)-还需要研究@abrahamson响应。正如你正确推断的,我迷失在两个想法的交叉点上-如何解析有时是JSON数组,有时是单个JSON对象。使用前一半的“技巧”,它现在可以工作了。但仅限于“扩展”案例。现在我需要使用您的第二个()
技巧来处理“shorten”案例。我会带着我的最新进展回来(希望很快)。谢谢正如您正确推断的那样,我迷失在两个想法的交叉点上——如何解析有时是JSON数组,有时是单个JSON对象的东西。使用前一半的“技巧”,它现在可以工作了。但仅限于“扩展”案例。现在我需要使用您的第二个()
技巧来处理“shorten”案例。我会带着我的最新进展回来(希望很快)。谢谢