的json看起來像這樣與埃宋解析JSON時,首先關鍵是可變
{
"NW011": {
"version": 1.0,
"compatiblehardware": ["N21"],
"description": "TWIN"
}
}
的問題是,關鍵NW011是可變的。當我開始解析時,我知道這個鍵,但我不知道如何聲明aeson的數據類型和實例。
的json看起來像這樣與埃宋解析JSON時,首先關鍵是可變
{
"NW011": {
"version": 1.0,
"compatiblehardware": ["N21"],
"description": "TWIN"
}
}
的問題是,關鍵NW011是可變的。當我開始解析時,我知道這個鍵,但我不知道如何聲明aeson的數據類型和實例。
您可以將您的JSON解析爲(地圖文本),其中Thing是您的記錄類型。測試的代碼如下:
{-# LANGUAGE DeriveGeneriC#-}
import Data.ByteString.Lazy as BSL
import Data.Text (Text)
import Data.Aeson
import Data.Map (Map)
import GHC.Generics
-- Record representing a single thing
data Thing =
Thing {
version :: Float,
compatiblehardware :: [Text],
description :: Text
} deriving (Show, Generic)
instance FromJSON Thing
main :: IO()
main = do
inh <- BSL.readFile "json.txt"
case decode inh :: Maybe (Map Text Thing) of
Just parsed -> print parsed
Nothing -> print "Unparsable"
這裏的測試數據, 「json.txt」:
{
"NW011": {
"version": 1.0,
"compatiblehardware": ["N21"],
"description": "TWIN"
},
"NW012": {
"version": 2.4,
"compatiblehardware": ["N21", "N22"],
"description": "TURBO"
}
}
Aeson中的JSON對象只是哈希映射。如果你願意,你可以提取所有的鍵。在這種情況下,你不能擁有一個數據類型,其中的字段以JSON密鑰命名,因爲它不一致,但仍然可以解析和使用JSON。未經測試的代碼如下。
import Data.Aeson
import Data.Text (Text)
import Data.HashMap.Strict as HMap
parseMyJSON :: Value -> Parser (Text,Value)
parseMyJSON (Object v) =
case HMap.toList v of
[(k,v)] -> (k, v)
_ -> fail "More than one key - who sent this thing?"
parseMyJSON _ = fail "Incorrect JSON - expected an object."
首先,你需要聲明模型你的數據實際上是一種數據類型。我認爲「NW011」實際上充當了一種字段,儘管有人將JSON格式提升爲看起來像記錄名稱。所以:
data Thing = Thing {
thingName :: Text, -- E.g. "NW011"
thingVersion :: Text, -- I'm assuming that "Version" could include "1.2.3A" and hence should be a string, not a number.
thingCompatible :: [Text],
thingDescription :: Text
}
現在是實例。通常當你解析JSON時,你會得到一個帶有固定字段名的對象,所以你可以使用「。:」來提取它們。在這種情況下,您的最外層結構是單場對象,其中該字段被稱爲「NW011」幷包含更傳統的對象。
所以你需要編寫一個檢索這個原始對象的實例。我假設你可以有幾個這樣的對象,如「NW012」等不同的名稱,所以實際上你的外部結構就是這些東西的列表。如果你解析你給出的例子,那麼你將得到一個單元素列表。
在Aeson中,「Object」是一個查找表(即HashMap)的包裝。所以你需要做的就是遍歷HashMap,通過名字來提取對象。所以答案應該是這樣的(雖然我沒有試過編譯它):
import qualified Data.HashMap.Strict as H
instance FromJSON [Thing] where
parseJSON (Object v) = mapM parseItem $ H.toList v
-- 'v' is a HashMap containing a key "NW011".
where
parseItem (k, Object v2) =
-- "v2" is a HashMap containing keys for the fixed field names ("version" etc.)
Thing k <$>
v .: "version" <*>
v .: "compatiblehardware" <*>
v .: "description"
請注意,這只是給出了成功案例。你的代碼還應該包括parseJSON和parseItem的參數不是對象的情況。
嗨。感謝您的回覆。我對哈斯克爾很陌生,幾天來一直在努力解決這個問題(除了我使用YAML)。我似乎遇到了v和v2類型的問題。你有什麼機會可以提供一些指針?本代碼和錯誤[gist](https://gist.github.com/jeffutter/4133c49d8dd52ce36d8efb0b6be8ac4f)。謝謝 –
這裏有很多事情要做。首先,我的錯誤是,我忘了H.mapWithKey返回一個新的HashMap,而不是一個列表,並且Aeson使用嚴格的hashmaps。查看上面更新的文本,包括對parseItem的更改。除此之外,Gist中的代碼似乎是解析在對象中具有「名稱」字段的組,而不是您描述的變量鍵,所以這不能解決您的問題。 –
感謝您的更新。它現在開始有意義並且幾乎可行。我已經更新了要點並添加了示例YAML。我得到了一些關於期望組的信息,但是卻得到了一個列表。 –
版本似乎是一個數字。但通常版本是字符串,而不是數字。你將在1.2.3A版本中做些什麼? –
如果您在開始解析之前知道密鑰,則可以定義一個將該密鑰作爲參數並返回解析器的函數。 –