为什么我的代码将已排序的JSON文件解析为未排序的列表? | Haskell和Aeson

时间:2018-10-17 14:43:32

标签: json haskell aeson

我正在尝试解析一个看起来像这样的嵌套JSON文件

{
    "Meta Data": {
        "1: Symbol": "MSFT",
        "2: Indicator": "Relative Strength Index (RSI)",
        "3: Last Refreshed": "2018-10-17 10:31:05",
        "4: Interval": "weekly",
        "5: Time Period": 10,
        "6: Series Type": "open",
        "7: Time Zone": "US/Eastern Time"
    },
    "Technical Analysis: RSI": {
        "2018-10-17 10:31:05": {
            "RSI": "54.5653"
        },
        "2018-10-12": {
            "RSI": "63.0279"
        },
        "2018-10-05": {
            "RSI": "74.7519"
        },
        "2018-09-28": {
            "RSI": "72.1573"
        },
        "2018-09-21": {
            "RSI": "74.8123"
        },
        "2018-09-14": {
            "RSI": "66.7116"
        },
        "2018-09-07": {
            "RSI": "75.8051"
          ...
          ...
          ... and so on

我的目标是获得最新/最高的“ RSI”值。由于键(日期)是可变的,因此我试图在不匹配键的情况下获取它。


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

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

module Tesst where

import           Data.Aeson
import qualified Data.ByteString.Lazy as B
import           Data.HashMap.Strict  as S
import           Data.Text
import           Data.Time
import           GHC.Exts
import           GHC.Generics
import           Network.HTTP.Conduit (simpleHttp)

newtype TechnicalAnalysis = TechnicalAnalysis {
                   rsiQuote  :: Object
                   } deriving (Show, Generic)


instance FromJSON TechnicalAnalysis where
  parseJSON = withObject "RSI Quote" $
    \o -> do
      rsiQuote <- o        .: "Technical Analysis: RSI"
      return TechnicalAnalysis {..}

main :: IO ()
main = do
  putStrLn "Which ticker? "
  symbolToQuote <- getLine
  d <- (eitherDecode <$> simpleHttp ("https://www.alphavantage.co/query?function=RSI&interval=weekly&time_period=10&series_type=open&apikey=FI2KMDSAE&symbol=" ++ symbolToQuote)) :: IO (Either String TechnicalAnalysis)
  case d of
    Left e   -> print e
    Right qt -> print $ Prelude.map (\(a, b) -> (read (unpack a) :: Day, b)) (S.toList (rsiQuote qt))

我要运行的代码是这样的:

[(2005-09-02,Object (fromList [("RSI",String "60.8860")])),(2017-12-29,Object (fromList [("RSI",String "71.1921")])),(2011-09-16,Object (fromList [("RSI",String "50.3489")])),(2000-05-12,Object (fromList [("RSI",String "33.6402")]))...

如您所见,所有值都是乱序的。


我的问题是:

  1. 为什么不按原样打印该JSON文件?
  2. 是否可以获取最新的RSI值而无需对 结果?

2 个答案:

答案 0 :(得分:0)

我能够按照威廉姆斯的指示(在评论中)解决问题。

angular

这是关键

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

module Tesst where

import           Data.Aeson
import qualified Data.ByteString.Lazy as B
import           Data.HashMap.Strict  as S
import           Data.List
import           Data.Text
import           Data.Time
import           GHC.Exts
import           GHC.Generics
import           Network.HTTP.Conduit (simpleHttp)


newtype TechnicalAnalysis = TechnicalAnalysis {
                   rsiQuote  :: Object
                   } deriving (Show, Generic)


instance FromJSON TechnicalAnalysis where
  parseJSON = withObject "RSI Quote" $
    \o -> do
      rsiQuote <- o        .: "Technical Analysis: RSI"
      return TechnicalAnalysis {..}

main :: IO ()
main = do
  putStrLn "Which ticker? "
  symbolToQuote <- getLine
  d <- (eitherDecode <$> simpleHttp ("https://www.alphavantage.co/query?function=RSI&interval=weekly&time_period=10&series_type=open&apikey=FI2KMC2DSAE&symbol=" ++ symbolToQuote)) :: IO (Either String TechnicalAnalysis)
  case d of
    Left e   -> print e
    Right qt -> do
      let l = S.toList (rsiQuote qt)
      print $ maximumBy (\(a, _) (b, _) -> compare a b) l

答案 1 :(得分:0)

您已选择本质上为动态类型的Object作为语义对象。选择更好的东西; Map Day Double,例如,或者ArgMax Day Double,如果您真的只想要最新的RSI值,而不会查看其他任何内容。

例如:

import Control.Monad
import Data.Aeson
import Data.Aeson.Types
import Data.HashMap.Strict as S
import Data.List.NonEmpty
import Data.Semigroup
import Data.Text
import Data.Time

-- can't use readMaybe, 2018-10-17 10:31:05 has extra non-Day gunk at the end
jsonReadText :: Read a => String -> Text -> Parser a
jsonReadText err t = case reads (unpack t) of
    (v,_):_ -> return v
    _ -> fail $ "could not parse " ++ unpack t ++ " as " ++ err

newtype RSI = RSI Double deriving Show

instance FromJSON RSI where
    parseJSON = withObject "object with RSI key" $ \o -> do
        t <- o .: pack "RSI"
        RSI <$> jsonReadText "Double" t

newtype LatestRSI = LatestRSI (ArgMax Day RSI) deriving Show

instance FromJSON LatestRSI where
    parseJSON = withObject "RSI Quote" $ \o -> do
        quoteMap <- o .: pack "Technical Analysis: RSI"
        quotes <- forM (S.toList quoteMap) $ \(dayText, rsiValue) -> do
            day <- jsonReadText "Day" dayText
            rsi <- parseJSON rsiValue
            return (Max (Arg day rsi))
        case quotes of
            [] -> fail "expected non-empty collection of RSI quotes"
            q:qs -> return . LatestRSI . sconcat $ q :| qs

ghci中的用法示例:

> bs <- Data.ByteString.Lazy.readFile "test.json"
> eitherDecode bs :: Either String LatestRSI
Right (LatestRSI (Max {getMax = Arg 2018-10-17 (RSI 54.5653)}))