Aeson Haskell-是否有更好的解析历史数据的方法? [英] Haskell, Aeson - Is there a better way of parsing historical data?

查看:106
本文介绍了Aeson Haskell-是否有更好的解析历史数据的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据历史数据",我只是将日期作为关键,将当天的值作为值.

By 'historical data' I just mean dates as key, and value on that day as value.

例如,通常政府机构或uni的研究部门以这种格式来编写有关地震,降雨,市场动向等的日期

For example, often govt institutes or uni's research division compile date about earthquakes, rainfalls, market movement, etc. in this format

 {
        "Meta Data": {
            "1: Country": "SomeCountry",
            "2: Region": "SomeRegion",
            "3: Latest Recording": "2018-11-16"
        },
        "EarthQuakes": {
            "2018-11-16": {
                "Richter": "5.2508"
            },
            "2018-11-09": {
                "Richter": "4.8684"
            },
            "2018-11-02": {
                "Richter": "1.8399"
            },
    ...
    ...
    ...
            "1918-11-02": {
                "Richter": "1.8399"
            }
}


通常它将有一个元数据"部分,其他部分将包含值/数据.


Usually it'll have a "Meta Data" section and other one would contain the values/data.

作为初学者,我知道解析此类文档的两种方法.

I as a beginner know of two ways to parse these type of documents.

您可以使用Aeson文档中显示的常规解析,在其中定义这样的数据类型

Either you go with general parsing shown in Aeson's documentation where you define data types like this

Data MetaData = MetaData { country :: String, region :: String, latestRec :: String } deriving (Show, Eq, Generic)

使其成为FromJSON

instance FromJSON MetaData where
  parseJSON = withObject "MetaData" $
    \v -> do
       metaData  <- v        .: pack "Meta Data"
       country   <- metaData .: pack "1: Country"
       region    <- metaData .: pack "2: Region"
       latestRec <- metaData .: pack "3: Latest Recording"
       return MetaData{..}

当然启用了RecordWildCardDeriveGenerics扩展名.

我看到的这种方法的问题是,对于"EarthQuakes"部分来说,它不容易实现.

The problem I see with this approach is that it can't be easily implemented for the "EarthQuakes" section.

我必须定义每个日期

earthQuakes <- v .: "EarthQuakes"
date1 <- earthQuakes .: "2018-11-16"
date2 <- earthQuakes .: "2018-11-06"
date3 <- earthQuakes .: "2018-11-02"
...
...
dateInfinity <- earthQuakes .: "1918-11-16"


一种更好的方法是通过将链接解码为Object类型

thisFunction = do
    linksContents <- simpleHttp "somelink"
    let y = fromJust (decode linksContents :: Object)
        z = aLotOfFunctionCompositions y
    return z

,其中aLotOfFunctionCompositions首先将Object转换为具有[(k, v)]对的HashMap.然后,我将映射一个unConstruct函数以从默认构造函数(如

where the aLotOfFunctionCompositions would first convert Object to maybe HashMap having [(k, v)] pairs. Then I would map an unConstruct function to get the value out of default constructors like

unConstruct (DefaultType value) = case (DefaultType value) of
             DefaultType x -> x

最后,您会得到一个不错的清单!

and finally you would get a nice list!

这种方法的问题是aLotOfFunctionComposition.

那只是一个例子!但实际上,它看起来像这样丑陋且不可读

That is just an example! But in reality it can look as ugly and unreadable as this

let y = Prelude.map (\(a, b) -> (decode (encode a) :: Maybe String, decode (encode (snd (Prelude.head b))) :: Maybe String)) x
      z = Prelude.map (\(a, b) -> (fromJust a, fromJust b)) y
      a = Prelude.map (\(a, b) -> (a, read b :: Double)) z
      b = Prelude.map (\(a, b) -> (Prelude.filter (/= '-') a, b)) a
      c = Prelude.map (\(a, b) -> (read a :: Int, b)) b

这是我编写的工作代码的片段.

This is snippet from a working code I made.

所以我的问题是:是否存在一种更好/更清洁的方式来解码这些类型的JSON文件,而在这些文件中您拥有许多日期"键,并且需要将它们解析为可行的数据类型?

So my question is this: Is there a better/cleaner way of decoding these sorts of JSON files where you have a lot of "dates" keys and you need to parse them into workable datatypes?

推荐答案

在数据类型中放入Map. Aeson将Map k v与对象进行相互转换,其中v通过其自己的To-/From-JSON实例进行编码/解码,而kTo进行编码/解码. -/From-JSONKey s.事实证明,Day(来自time软件包)具有完全合适的To-/From-JSONKey实例.

Put a Map in your data type. Aeson translates Map k vs to/from objects, where the vs are en-/de-coded via their own To-/From-JSON instances and the ks by To-/From-JSONKeys. It turns out that Day (from the time package) has perfectly suitable To-/From-JSONKey instances.

data EarthquakeData = EarthquakeData {
    metaData :: MetaData,
    earthquakes :: Map Day Earthquake
} deriving (Eq, Show, Generic)

instance FromJSON EarthquakeData where
    parseJSON = withObject "EarthquakeData $ \v ->
        EarthquakeData <$> v .: "Meta Data"
        -- Map k v has a FromJSON instance that just does the right thing
        -- so just get the payloads with (.:)
        -- all this code is actually just because your field names are really !#$@~??
        -- not an Aeson expert, maybe there's a better way
                       <*> v .: "EarthQuakes"
instance ToJSON EarthquakeData where
    toJSON EarthquakeData{..} = object [ "Meta Data"   .= metaData
                                       , "EarthQuakes" .= earthquakes
                                       ]

data MetaData = MetaData { country :: String, region :: String, latestRec :: Day } deriving (Eq, Show)
instance FromJSON MetaData where
    parseJSON = withObject "MetaData" $ \v ->
        -- if you haven't noticed, applicative style is much neater than do
        -- using OverloadedStrings avoids all the pack-ing static
        MetaData <$> v .: "1: Country"
                 <*> v .: "2: Region"
                 <*> v .: "3: Latest Recording"
instance ToJSON MetaData where
    toJSON MetaData{..} = object [ "1: Country"          .= country
                                 , "2: Region"           .= region
                                 , "3: Latest Recording" .= latestRec
                                 ]
    toEncoding MetaData{..} = pairs $ "1: Country"          .= country
                                   <> "2: Region"           .= region
                                   <> "3: Latest Recording" .= latestRec

data Earthquake = Earthquake { richter :: Double } deriving (Eq, Show)
-- Earthquake is a bit funky because your JSON apparently has
-- numbers inside strings?
-- only here do you actually need monadic operations
instance FromJSON Earthquake where
    parseJSON = withObject "Earthquake" $ \v ->
        do string <- v .: "Richter"
           stringNum <- parseJSON string
           case readMaybe stringNum of
             Just num -> return $ Earthquake num
             Nothing -> typeMismatch "Double inside a String" string
instance ToJSON Earthquake where
    toJSON = object . return . ("Richter" .=) . show . richter
    toEncoding = pairs . ("Richter" .=) . show . richter

我已经针对您的示例JSON进行了测试,它似乎成功地往返了encodedecode.

I've tested this against your example JSON, and it appears to roundtrip encode and decode successfully.

这篇关于Aeson Haskell-是否有更好的解析历史数据的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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