2016-03-02 37 views
1

的json看起來像這樣與埃宋解析JSON時,首先關鍵是可變

{ 
    "NW011": { 
     "version": 1.0, 
     "compatiblehardware": ["N21"], 
     "description": "TWIN" 
    } 
} 

的問題是,關鍵NW011是可變的。當我開始解析時,我知道這個鍵,但我不知道如何聲明aeson的數據類型和實例。

+0

版本似乎是一個數字。但通常版本是字符串,而不是數字。你將在1.2.3A版本中做些什麼? –

+1

如果您在開始解析之前知道密鑰,則可以定義一個將該密鑰作爲參數並返回解析器的函數。 –

回答

1

您可以將您的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" 
    } 
} 
3

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." 
5

首先,你需要聲明模型你的數據實際上是一種數據類型。我認爲「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的參數不是對象的情況。

+0

嗨。感謝您的回覆。我對哈斯克爾很陌生,幾天來一直在努力解決這個問題(除了我使用YAML)。我似乎遇到了v和v2類型的問題。你有什麼機會可以提供一些指針?本代碼和錯誤[gist](https://gist.github.com/jeffutter/4133c49d8dd52ce36d8efb0b6be8ac4f)。謝謝 –

+0

這裏有很多事情要做。首先,我的錯誤是,我忘了H.mapWithKey返回一個新的HashMap,而不是一個列表,並且Aeson使用嚴格的hashmaps。查看上面更新的文本,包括對parseItem的更改。除此之外,Gist中的代碼似乎是解析在對象中具有「名稱」字段的組,而不是您描述的變量鍵,所以這不能解決您的問題。 –

+0

感謝您的更新。它現在開始有意義並且幾乎可行。我已經更新了要點並添加了示例YAML。我得到了一些關於期望組的信息,但是卻得到了一個列表。 –