簡體   English   中英

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)

其中名稱對應於“ 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)

現在我可以編譯並運行

test "path/to/json" 

在ghci中,在所有X都為“ stringX1” =“ stringA1”的情況下,它會打印Mydat的列表。實際上,“ stringX1”有兩個值,因此,除了hackyness之外,這還不能令人滿意。 必須有更好的方法來做到這一點。 我知道我可能需要編寫自己的解析器,但是我對它的工作方式感到困惑,因此任何建議都會很棒。 提前致謝。

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只是部分內容,通常不是一個好主意。 但我會留給您充實一個更安全的版本:)

正如我所評論的,最好的辦法可能是使您的JSON文件格式正確,以使float字段應該真正是float而不是字符串。

如果不是這種選擇,我建議您說出JSON文件似乎表示盡可能簡單的類型(但不包含dynamic Object ),然后將其轉換為您實際想要的類型。

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格式的字符串表示),這將完全崩潰,並且當您濾除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 -> []

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM