Haskell-Aeson:获得“无"尝试解码JSON URL Req时 [英] Haskell - Aeson : Getting "Nothing" when trying to decode JSON URL Req

查看:141
本文介绍了Haskell-Aeson:获得“无"尝试解码JSON URL Req时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对haskell还是比较陌生,现在我正试图加深了解并试图适应不同的流行图书馆.

I'm relatively new to haskell and right now I'm trying to get a deeper understanding and trying to get used to different popular libraries.

现在我正在尝试"aeson".

Right now I'm trying "aeson".

我想做的是解析 https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=MSFT&apikey=demo

这就是它的样子

{
    "Global Quote": {
        "01. symbol": "MSFT",
        "02. open": "105.3500",
        "03. high": "108.2400",
        "04. low": "105.2700",
        "05. price": "107.6000",
        "06. volume": "23308066",
        "07. latest trading day": "2018-10-11",
        "08. previous close": "106.1600",
        "09. change": "1.4400",
        "10. change percent": "1.3564%"
    }
}

这是我到目前为止所得到的

This is what I've got so far

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

import           Data.Aeson
import qualified Data.ByteString.Lazy as B
import           GHC.Exts
import           GHC.Generics
import           Network.HTTP
import           Network.URI

jsonURL :: String
jsonURL = "http://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=MSFT&apikey=demo"

getRequest_ :: HStream ty => String -> Request ty
getRequest_ s = let Just u = parseURI s in defaultGETRequest_ u

jsonReq = getRequest_ jsonURL

data Quote = Quote {quote         :: String,
                    symbol        :: String,
                    open          :: Float,
                    high          :: Float,
                    low           :: Float,
                    price         :: Float,
                    volume        :: Float,
                    ltd           :: String,
                    previousClose :: Float,
                    change        :: Float,
                    changePerct   :: Float
                   } deriving (Show, Generic)

instance FromJSON Quote
instance ToJSON Quote


main :: IO ()
main = do
  d <- simpleHTTP jsonReq
  body <- getResponseBody d
  print (decode body :: Maybe Quote)

我在做什么错了?

答案中的固定版本.

推荐答案

首先,Aeson不是对于初学者来说最简单的库.当然,还有更多困难的问题,但是前提是您已经对语言有了很多了解.首先,您没有选择最简单的任务".我知道这可能令人惊讶,并且您可能认为解析JSON应该很简单,但是解析具有强类型保证的JSON实际上并不是那么简单.

First off: Aeson is not the easiest library for a beginner. There are more difficult ones, sure, but it supposes you already a fair number of things about the language. You didn't pick the "simplest task" to begin with. I know this can be surprising, and you might think that parsing JSON should be simple, but parsing JSON with strong type guarantees is actually not that simple.

但是我可以告诉您一些帮助:

But here's what I can tell you to help you a bit:

  • 首先,使用eitherDecode而不是decode:您会收到一条错误消息,而不是简单的Nothing,这将对您有所帮助.

  • First, use eitherDecode rather than decode: you will get an error message rather than simply Nothing, which will help you a bit.

通过Generic派生是很整洁的,而且通常可以节省时间,但这也不是不可思议的.对象键的名称和数据类型字段的名称必须完全匹配.遗憾的是,这里不是这种情况,由于haskell语法,您无法像对象的键一样命名字段.最好的解决方案是手动实现FromJSON(请参见下面的推荐链接).了解通用FromJSON的期望值"的一个好方法是也派生ToJSON,创建虚拟Quote并查看encode的结果.

Deriving through Generic is neat and very often, a time saver, but it's not magic either. The name of the object key and the name of your datatype fields have to match exactly. Sadly, this is not the case here and due to haskell syntax, you couldn't name your fields like the keys of the object. Your best solution is to implement FromJSON manually (see the recommended link below). A good way to see "what is expect" by the generic FromJSON is to also derive ToJSON, create a dummy Quote and see the result of encode.

您的第一个字段(quote)不是对象本身的键,而是该对象的名称.因此,您就有了动态键(此处是全局报价").再一次,这通常是您要手动编写FromJSON实例的情况.

Your first field (quote) is not a key of the object itself, but rather the name of this object. So you have dynamic keys ("Global Quote" being one here). Once again, this typically a case where you want to write the FromJSON instance manually.

我建议您阅读由Artyom Kazak在Aeson上撰写的著名的教程.这将极大地帮助您,并且可能是我能提供的最佳建议.

I recommend you read this famous tutorial written by Artyom Kazak on Aeson. This will help you tremendously and is probably the best advice I can give.

对于您的手动实例,假设它恰好是您要解析的文档,并且只有全局引用"要处理,那么它看起来或多或少是这样的:

For your manual instance, supposing it was exactly the document you want to parse and you had only the "Global Quote" to deal with, it would look more or less like this:

instance ToJSON Quote where
  parseJSON = withObject "Document" $
    \d -> do
        glob <- d .: "Global Quote"
        withObject "Quote" v (\gq ->
          Quote <$> gq .: "01. symbol"
                <*> pure "Global Quote"
                <*> gq .: "02. open"
                <*> gq .: "03. high"
          -- ... and so on
         ) v

(这不是写它的最漂亮的方法,也不是最好的方法,但是应该是一种可能的方法.)

(It's not the most pretty way, nor the best way, to write it, but it should be one possible way).

还请注意,正如一位敏锐的评论者所写,字段的类型并不总是与示例JSON文档的类型一致. "volume"是Int(字节限制的int),可能是Integer(数学"整数,无限制),但不是Float.您的"ltd"可以解析为字符串-但它可能应该是一个日期(Data.Time中的Day将是首选-它已经具有FromJSON实例,因此有可能应该按原样进行解析).变更百分比很可能无法像Float那样解析,您需要为此类型编写一个专用的解析器(并确定要如何存储它-Ratio是一个可能的解决方案).

Also note that, as an astute commenter wrote, the types of your fields are not always aligned with the type of your example JSON document. "volume" is an Int (byte-limited int), potentially an Integer ("mathematical" integer, no bound), but not a Float. Your "ltd" can be parsed a string - but it probably should be a date (Day from Data.Time would be the first choice - it already has a FromJSON instance so chances are it should be parseable as is). Change percent is most likely not parseable as Float like that, you'll need to write a dedicated parser for this type (and decide how you want to store it - Ratio is a potential solution).

这篇关于Haskell-Aeson:获得“无"尝试解码JSON URL Req时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆