JSON (Data.Aeson) 有问题
Trouble with JSON (Data.Aeson)
我是 Haskell 的新手,为了学习这门语言,我正在从事一个涉及处理 JSON 的项目。我目前感觉 Haskell 是这份工作的错误语言,但这不是重点。
几天来我一直在努力理解它是如何工作的。我已经搜索过,但我发现的一切似乎都不起作用。这是问题所在:
我有一些 JSON 格式如下:
>>>less "path/to/json"
{
"stringA1_stringA2": {"stringA1":floatA1,
"stringA2":foatA2},
"stringB1_stringB2": {"stringB1":floatB1,
"stringB2":floatB2}
...
}
这里的floatX1和floatX2实际上是"0.535613567"、"1.221362183"等形式的字符串,我要做的是将其解析成如下数据
data Mydat = Mydat { name :: String, num :: Float} deriving (Show)
其中 name 对应于 "stringX1_stringX2",num 对应于 floatX1 for X = A,B,...
到目前为止,我已经达到了一个 'solution' 感觉相当hackish和令人费解并且无法正常工作。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
import Data.Functor
import Data.Monoid
import Data.Aeson
import Data.List
import Data.Text
import Data.Map (Map)
import qualified Data.HashMap.Strict as DHM
--import qualified Data.HashMap as DHM
import qualified Data.ByteString.Lazy as LBS
import System.Environment
import GHC.Generics
import Text.Read
data Mydat = Mydat {name :: String, num :: Float} deriving (Show)
test s = do
d <- LBS.readFile s
let v = decode d :: Maybe (DHM.HashMap String Object)
case v of
-- Just v -> print v
Just v -> return $ Prelude.map dataFromList $ DHM.toList $ DHM.map (DHM.lookup "StringA1") v
good = ['1','2','3','4','5','6','7','8','9','0','.']
f x = elem x good
dataFromList :: (String, Maybe Value) -> Mydat
dataFromList (a,b) = Mydat a (read (Prelude.filter f (show b)) :: Float)
现在我可以编译它 运行
test "path/to/json"
在 ghci 中,它会在 "stringX1"="stringA1" 的情况下为所有 X 打印 Mydat 的列表。实际上 "stringX1" 有两个值,所以除了 hackyness这并不令人满意。必须有更好的方法来做到这一点。我知道我可能需要编写自己的解析器,但我对它的工作原理感到困惑,所以任何建议都会很好。提前致谢。
正如我评论的那样,最好的办法可能是使您的 JSON 文件格式正确,因为浮点字段实际上应该是浮点数,而不是字符串。
如果那不是一个选项,我会建议您尽可能简单地表达 JSON 文件 似乎代表 的类型(但没有动态 Object
s),然后将其转换为您实际需要的类型。
import Data.Map (Map)
import qualified Data.Map as Map
type GarbledJSON = Map String (Map String String)
-- ^ you could also stick with hash maps for everything, but
-- usually `Map` is actually more sensible in Haskell.
data MyDat = MyDat {name :: String, num :: Float} deriving (Show)
test :: FilePath -> IO [MyDat]
test s = do
d <- LBS.readFile s
case decode d :: Maybe GarbledJSON of
Just v -> return [ MyDat iName ( read . filter (`elem`good)
$ iVals Map.! valKey )
| (iName, iVals) <- Map.toList v
, let valKey = takeWhile (/='_') iName ]
请注意,如果任何项目不包含名称的第一部分作为 float 格式的字符串,这将完全崩溃,并且当您过滤掉不是 [=13 的字符时,可能会给出虚假项目=].如果你只是想忽略任何格式错误的项目(这也不是一个非常干净的方法......),你可以这样做:
test :: FilePath -> IO [MyDat]
test s = do
d <- LBS.readFile s
return $ case decode d :: Maybe GarbledJSON of
Just v -> [ MyDat iName iVal
| (iName, iVals) <- Map.toList v
, let valKey = takeWhile (/='_') iName
, Just iValStr <- [iVals Map.!? valKey]
, [(iVal,"")] <- [reads iValStr] ]
Nothing -> []
您 JSON 的结构非常糟糕,但这是一个基本的工作解决方案:
#!/usr/bin/env stack
-- stack --resolver lts-11.5 script --package containers --package aeson
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Map as Map
import qualified Data.Aeson as Aeson
data Mydat = Mydat { name :: String
, num :: Float
} deriving (Show)
instance Eq Mydat where
(Mydat _ x1) == (Mydat _ x2) = x1 == x2
instance Ord Mydat where
(Mydat _ x1) `compare` (Mydat _ x2) = x1 `compare` x2
type MydatRaw = Map.Map String (Map.Map String String)
processRaw :: MydatRaw -> [Mydat]
processRaw = Map.foldrWithKey go []
where go key value accum =
accum ++ (Mydat key . read <$> Map.elems value)
main :: IO ()
main =
do let json = "{\"stringA1_stringA2\":{\"stringA1\":\"0.1\",\"stringA2\":\"0.2\"}}"
print $ fmap processRaw (Aeson.eitherDecode json)
请注意 read
是部分的,通常不是一个好主意。但我会留给你充实一个更安全的版本:)
我是 Haskell 的新手,为了学习这门语言,我正在从事一个涉及处理 JSON 的项目。我目前感觉 Haskell 是这份工作的错误语言,但这不是重点。
几天来我一直在努力理解它是如何工作的。我已经搜索过,但我发现的一切似乎都不起作用。这是问题所在:
我有一些 JSON 格式如下:
>>>less "path/to/json"
{
"stringA1_stringA2": {"stringA1":floatA1,
"stringA2":foatA2},
"stringB1_stringB2": {"stringB1":floatB1,
"stringB2":floatB2}
...
}
这里的floatX1和floatX2实际上是"0.535613567"、"1.221362183"等形式的字符串,我要做的是将其解析成如下数据
data Mydat = Mydat { name :: String, num :: Float} deriving (Show)
其中 name 对应于 "stringX1_stringX2",num 对应于 floatX1 for X = A,B,...
到目前为止,我已经达到了一个 'solution' 感觉相当hackish和令人费解并且无法正常工作。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
import Data.Functor
import Data.Monoid
import Data.Aeson
import Data.List
import Data.Text
import Data.Map (Map)
import qualified Data.HashMap.Strict as DHM
--import qualified Data.HashMap as DHM
import qualified Data.ByteString.Lazy as LBS
import System.Environment
import GHC.Generics
import Text.Read
data Mydat = Mydat {name :: String, num :: Float} deriving (Show)
test s = do
d <- LBS.readFile s
let v = decode d :: Maybe (DHM.HashMap String Object)
case v of
-- Just v -> print v
Just v -> return $ Prelude.map dataFromList $ DHM.toList $ DHM.map (DHM.lookup "StringA1") v
good = ['1','2','3','4','5','6','7','8','9','0','.']
f x = elem x good
dataFromList :: (String, Maybe Value) -> Mydat
dataFromList (a,b) = Mydat a (read (Prelude.filter f (show b)) :: Float)
现在我可以编译它 运行
test "path/to/json"
在 ghci 中,它会在 "stringX1"="stringA1" 的情况下为所有 X 打印 Mydat 的列表。实际上 "stringX1" 有两个值,所以除了 hackyness这并不令人满意。必须有更好的方法来做到这一点。我知道我可能需要编写自己的解析器,但我对它的工作原理感到困惑,所以任何建议都会很好。提前致谢。
正如我评论的那样,最好的办法可能是使您的 JSON 文件格式正确,因为浮点字段实际上应该是浮点数,而不是字符串。
如果那不是一个选项,我会建议您尽可能简单地表达 JSON 文件 似乎代表 的类型(但没有动态 Object
s),然后将其转换为您实际需要的类型。
import Data.Map (Map)
import qualified Data.Map as Map
type GarbledJSON = Map String (Map String String)
-- ^ you could also stick with hash maps for everything, but
-- usually `Map` is actually more sensible in Haskell.
data MyDat = MyDat {name :: String, num :: Float} deriving (Show)
test :: FilePath -> IO [MyDat]
test s = do
d <- LBS.readFile s
case decode d :: Maybe GarbledJSON of
Just v -> return [ MyDat iName ( read . filter (`elem`good)
$ iVals Map.! valKey )
| (iName, iVals) <- Map.toList v
, let valKey = takeWhile (/='_') iName ]
请注意,如果任何项目不包含名称的第一部分作为 float 格式的字符串,这将完全崩溃,并且当您过滤掉不是 [=13 的字符时,可能会给出虚假项目=].如果你只是想忽略任何格式错误的项目(这也不是一个非常干净的方法......),你可以这样做:
test :: FilePath -> IO [MyDat]
test s = do
d <- LBS.readFile s
return $ case decode d :: Maybe GarbledJSON of
Just v -> [ MyDat iName iVal
| (iName, iVals) <- Map.toList v
, let valKey = takeWhile (/='_') iName
, Just iValStr <- [iVals Map.!? valKey]
, [(iVal,"")] <- [reads iValStr] ]
Nothing -> []
您 JSON 的结构非常糟糕,但这是一个基本的工作解决方案:
#!/usr/bin/env stack
-- stack --resolver lts-11.5 script --package containers --package aeson
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Map as Map
import qualified Data.Aeson as Aeson
data Mydat = Mydat { name :: String
, num :: Float
} deriving (Show)
instance Eq Mydat where
(Mydat _ x1) == (Mydat _ x2) = x1 == x2
instance Ord Mydat where
(Mydat _ x1) `compare` (Mydat _ x2) = x1 `compare` x2
type MydatRaw = Map.Map String (Map.Map String String)
processRaw :: MydatRaw -> [Mydat]
processRaw = Map.foldrWithKey go []
where go key value accum =
accum ++ (Mydat key . read <$> Map.elems value)
main :: IO ()
main =
do let json = "{\"stringA1_stringA2\":{\"stringA1\":\"0.1\",\"stringA2\":\"0.2\"}}"
print $ fmap processRaw (Aeson.eitherDecode json)
请注意 read
是部分的,通常不是一个好主意。但我会留给你充实一个更安全的版本:)