首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解析具有已知变量字段的JSON

解析具有已知变量字段的JSON
EN

Stack Overflow用户
提问于 2021-10-24 06:32:14
回答 2查看 143关注 0票数 2

我有一个Haskell query函数来获得最新的令牌价格

https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyQuotesLatest

该函数以令牌id作为arg,例如2010 for ADA。

代码语言:javascript
复制
import Data.Aeson
import Network.HTTP.Req

newtype Rate = Rate Double

query :: Int -> IO (Either Text Rate)
query tokenId = 
    let
        url = https queryPrefix /: "v1" /: "cryptocurrency" /: "quotes" /: "latest"
        idParam = "id" =: tokenId
        options = standardHeader <> idParam
    in
        runReq defaultHttpConfig $ do
            v <- req GET url NoReqBody jsonResponse options
            let responseCode = responseStatusCode v

            if isValidHttpResponse responseCode then do  
                case fromJSON $ responseBody v of
                    Success x -> pure $ Right x
                    Error e -> pure $ Left $ pack $ "Error decoding state: " <> e
            else
                pure $ Left $ pack ("Error with CoinMarketCap query 'Quotes Latest': " <> show responseCode <> ".  " <> show (responseStatusMessage v))              

然而,Json的产出以"2010“为关键:

代码语言:javascript
复制
{"status":
    {"timestamp":"2021-10-24T03:35:01.583Z","error_code":0,"error_message":null,"elapsed":163,"credit_count":1,"notice":null}
,"data":
    {"2010":
        {"id":2010
        ,"name":"Cardano"
        ,"symbol":"ADA"
        ,"slug":"cardano"
        ,"num_market_pairs":302,"date_added":"2017-10-01T00:00:00.000Z"
        ,"tags":["mineable","dpos","pos","platform","research","smart-contracts","staking","binance-smart-chain","cardano-ecosystem"]
        ,"max_supply":45000000000
        ,"circulating_supply":32904527668.666
        ,"total_supply":33250650235.236,"is_active":1
        ,"platform":null
        ,"cmc_rank":4
        ,"is_fiat":0
        ,"last_updated":"2021-10-24T03:33:31.000Z"
        ,"quote":
            {"USD":
                {"price":2.16109553945978
                ,"volume_24h":2048006882.386299
                ,"volume_change_24h":-24.06,"percent_change_1h":0.24896227
                ,"percent_change_24h":0.38920394
                ,"percent_change_7d":-0.97094597
                ,"percent_change_30d":-6.13245906
                ,"percent_change_60d":-21.94246757
                ,"percent_change_90d":63.56901345
                ,"market_cap":71109827972.785
                ,"market_cap_dominance":2.7813
                ,"fully_diluted_market_cap":97249299275.69,"last_updated":"2021-10-24T03:33:31.000Z"}}}}}

既然2010query的一个arg,我显然不想像这样的data.2010.quote.USD.price那样钻研:

代码语言:javascript
复制
instance FromJSON Rate where
    parseJSON = withObject "Rate" $ \o -> do
        dataO  <- o .: "data"
        _2010O <- dataO .: "2010" -- #############
        quoteO <- _2010O .: "quote"
        usdO <- quoteO .: "USD"
        price <- usdO .: "price"
        
        pure $ Rate price  

问:我怎样才能达到我想要的灵活性?我可以以某种方式将令牌id传递给parseJSON吗?或者也许有镜头-伊索技术使用通配符?.

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-10-24 16:06:49

我完全确信,"data"中的对象将永远只有一个键,我们可以获取对象,将其转换为一个值列表,如果列表为空或有多个值,则失败,否则继续解析。如下所示:

代码语言:javascript
复制
instance FromJSON Rate where
    parseJSON = withObject "Rate" $ \o -> do
        Object dataO  <- o .: "data" -- we expect an Object
         -- get the single value, it should be an Object itself
        [Object _2010O] <- pure $ Data.Foldable.toList dataO
        quoteO <- _2010O .: "quote"
        usdO <- quoteO .: "USD"
        price <- usdO .: "price"
        pure $ Rate price 

当没有键、多个键或值不是Object时,模式[Object _2010O] <-无法匹配,并通过埃森的ParserMonadFail实例给出一个解析错误。

我们也可以更明确一点:

代码语言:javascript
复制
instance FromJSON Rate where
    parseJSON = withObject "Rate" $ \o -> do
        Object dataO  <- o .: "data"
        let objects = Data.Foldable.toList dataO
        case objects of
            [Object _2010O] -> do
                quoteO <- _2010O .: "quote"
                usdO <- quoteO .: "USD"
                price <- usdO .: "price"
                pure $ Rate price  
            [_] -> fail "value is not Object"
            _ -> fail "zero or more than one key"
票数 2
EN

Stack Overflow用户

发布于 2021-10-24 18:12:28

遗憾的是,由于我提前知道了关键名称(示例中的“2010”),所以在解析时没有使用该信息。

问题是,类型类型方法,除了它们自己的参数外,只能访问编译时已知的静态信息。tokenId很可能是运行时信息,例如从配置文件中读取。

因此,一种解决方案可以减少对FromJSON实例的依赖。不是直接解析Rate,而是先解析一个Value (Aeson的Value有一个FromJSON实例),然后在FromJSON类型集之外的函数中执行ValueRate 解析,这个函数的作用域中有tokenId

不过,假设我们希望在最大程度上依赖FromJSON实例。我们可以尝试“返回一个接受我们仍然不知道的数据的函数”技巧,方法是定义一个类似于

代码语言:javascript
复制
-- we need to pass the tokenId to get the to the Rate
newtype RateWoTokenId = RateWoTokenId (Text -> Result Rate) 

和像这样的FromJSON实例

代码语言:javascript
复制
instance FromJSON RateWoTokenId where
    parseJSON = withObject "Rate" $ \o -> do
        dataO  <- o .: "data"
        pure $ RateWoTokenId $ \tokenId -> -- returning a function here!
            -- We continue parsing inside the function,
            -- because the tokenId is known there.
            flip Data.Aeson.Types.parse dataO $ \dataO -> do                   
                _2010O <- dataO .: Data.Aeson.Key.fromText tokenId
                quoteO <- _2010O .: "quote"
                usdO <- quoteO .: "USD"
                price <- usdO .: "price"
                pure $ Rate price          
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69694382

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档