Aeson Haskell-是否有更好的解析历史数据的方法? [英] Haskell, Aeson - Is there a better way of parsing historical data?
问题描述
根据历史数据",我只是将日期作为关键,将当天的值作为值.
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{..}
当然启用了RecordWildCard
和DeriveGenerics
扩展名.
我看到的这种方法的问题是,对于"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
实例进行编码/解码,而k
由To
进行编码/解码. -/From
-JSONKey
s.事实证明,Day
(来自time
软件包)具有完全合适的To
-/From
-JSONKey
实例.
Put a Map
in your data type. Aeson translates Map k v
s to/from objects, where the v
s are en-/de-coded via their own To
-/From
-JSON
instances and the k
s by To
-/From
-JSONKey
s. 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进行了测试,它似乎成功地往返了encode
和decode
.
I've tested this against your example JSON, and it appears to roundtrip encode
and decode
successfully.
这篇关于Aeson Haskell-是否有更好的解析历史数据的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!