根据“历史数据”,我只是将日期作为关键,将当天的值作为值。
例如,通常政府机构或uni的研究部门以这种格式来编写有关地震,降雨,市场动向等的日期
{
"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"
}
}
通常,它将有一个“元数据”部分,其他部分将包含值/数据。
作为初学者,我知道解析这类文档的两种方法。
您可以使用Aeson文档中显示的常规解析,在其中定义这样的数据类型
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”部分来说,它很难实现。
我必须定义每个日期
earthQuakes <- v .: "EarthQuakes"
date1 <- earthQuakes .: "2018-11-16"
date2 <- earthQuakes .: "2018-11-06"
date3 <- earthQuakes .: "2018-11-02"
...
...
dateInfinity <- earthQuakes .: "1918-11-16"
更好的方法是将链接解码为Object
类型,将所有数据解析为默认JSON值
thisFunction = do
linksContents <- simpleHttp "somelink"
let y = fromJust (decode linksContents :: Object)
z = aLotOfFunctionCompositions y
return z
其中aLotOfFunctionCompositions
首先将Object
转换为具有HashMap
对的[(k, v)]
。然后,我将映射一个unConstruct
函数以从默认构造函数(如
unConstruct (DefaultType value) = case (DefaultType value) of
DefaultType x -> x
最后,您会得到一个不错的清单!
此方法的问题是aLotOfFunctionComposition
。
那只是一个例子!但实际上,它看起来像这样丑陋且不可读
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
这是我编写的工作代码的片段。
所以我的问题是:是否存在一种更好/更清洁的方式来解码这类JSON文件,而在这些文件中您拥有许多“日期”键,并且需要将其解析为可行的数据类型?
答案 0 :(得分:2)
在数据类型中放入Map
。 Aeson将Map k v
与对象进行相互转换,其中v
通过它们自己的To
-// From
-JSON
实例进行编码/解码和k
乘To
-// From
-JSONKey
s。事实证明,Day
(来自time
包)完全适合To
-/ From
-JSONKey
实例。
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
。