JSON(Data.Aeson)问题

时间:2018-04-19 08:29:03

标签: json haskell 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,X = A,B,......

到目前为止,我已经达成了一个解决方案'这感觉相当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中,它打印了一个Mydat列表,其中&#34; stringX1&#34; =&#34; stringA1&#34;对于所有X.实际上,&#34; stringX1&#34;有两个值。所以除了hackyness之外,这并不令人满意。必须有更好的方法来做到这一点。我知道我需要编写自己的解析器,但我对这是如何工作感到困惑所以任何建议都会很棒。提前致谢。

2 个答案:

答案 0 :(得分:1)

你的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是部分的,通常不是一个好主意。但是我会留给你充实一个更安全的版本:)

答案 1 :(得分:0)

正如我评论的那样,最好的办法可能是让你的JSON文件格式正确,因为float字段应该是浮点数,而不是字符串。

如果那不是一个选项,我建议你尽可能简单地说明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 ]

请注意,如果任何项目不包含名称的第一部分作为浮点格式的字符串,这将完全崩溃,并且当您筛选出不是good的字符时可能会给出伪造的项目。如果您只是想忽略任何格式错误的项目(这也不是一个非常干净的方法......),您可以这样做:

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 -> []