JSON(Data.Aeson)有问题

JSON(Data.Aeson)有问题,json,haskell,aeson,Json,Haskell,Aeson,我是Haskell的新手,为了学习该语言,我正在从事一个涉及处理JSON的项目。我现在觉得Haskell是这份工作的错误语言,但这不是重点 几天来,我一直在努力理解它是如何工作的。我已经搜索过了,我发现的一切似乎都不起作用。问题是: 我有一些JSON格式如下: >>>less "path/to/json" { "stringA1_stringA2": {"stringA1":floatA1, "stringA2":foatA2}, "

我是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,表示X=A,B

到目前为止,我已经找到了一个“解决方案”,它让人感觉相当粗糙和复杂,而且不能正常工作

{-# 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)

在ghci中,如果“stringX1”=所有X的“stringA1”,它将打印Mydat的列表。实际上,“stringX1”有两个值,因此除了Hackynes之外,这并不令人满意。必须有更好的方法来做到这一点。我知道我可能需要编写自己的解析器,但我对它的工作原理感到困惑,所以任何建议都很好。提前谢谢

正如我所评论的,最好的方法可能是使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 ]

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)
#/usr/bin/env堆栈
--堆栈--解析器lts-11.5脚本--包容器--包aeson
{-#语言重载字符串}
导入符合条件的数据。映射为映射
导入符合条件的数据。Aeson作为Aeson
数据Mydat=Mydat{name::String
,num::Float
}派生(显示)
实例Eq Mydat,其中
(Mydat_ux1)==(Mydat_x2)=x1==x2
实例Ord Mydat在哪里
(Mydat_ux1)`compare`(Mydat_x2)=x1`compare`x2
键入MydatRaw=Map.Map字符串(Map.Map字符串)
processRaw::MydatRaw->[Mydat]
processRaw=Map.foldrWithKey go[]
其中go键值累计=
accum++(Mydat键。读取Map.elems值)
main::IO()
主要=
让json=“{\'stringA1\U stringA2\”:{\'stringA1\':\'0.1\',\'stringA2\':\'0.2\'}”
打印$fmap processRaw(Aeson.eitherDecode json)

请注意,
read
是局部的,通常不是一个好主意。但我将留给您来充实一个更安全的版本:)

您可能希望在这里定义自己的
FromJSON
实例。如果您想要一个如何做到这一点的示例,您应该在开头提供一个
“path/to/json”
的示例,我将进行编辑以使其更清楚。
“stringA1”:floatA1
不是有效的json。大概这应该是
“stringA1”:0.0
或者什么?对不起,是的,我也应该说清楚,floatX1实际上是类似于“0.238238273827”的字符串,但它最终应该是一个float。为什么在JSON文件中它们是字符串?为什么不
“stringA1”:0.238238273827
?感谢您的回复!不幸的是,我不想编译这两个。。“无法将类型“IO”与“[]”匹配预期类型:[LBS.ByteString]实际类型:IO LBS.ByteString”似乎是错误消息的关键部分。但我会接受下面乔丹的解决方案,因为它几乎奏效了,我想我能理解它。再次感谢您的快速回复!当然,它必须是类型签名中的
IO[MyDat]
,因为您正在读取文件。(虽然实际上最好将转换
GarbledJSON->[MyDat]
作为一个单独的纯函数。)感谢allot,这几乎满足了我的需要,我想我可以修改它来为我工作。
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 -> []
#!/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)