2013-07-24 156 views
10

我想解析哈希Kell中的JSON數據。經歷了大量的網站,這是我所能達到的最遠的地方。JSON解析哈斯克爾

data Address = Address { house :: Integer, street :: String, city :: String, state :: String, zip :: Integer } deriving (Show) 
data Person = Person { name :: String, age :: Integer, address :: Address } deriving (Show) 

getName :: Person -> String 
getName (Person n _ _) = n 

getAddress :: Person -> Address 
getAddress (Person _ _ a) = a 

getState :: Address -> String 
getState (Address _ _ _ s _) = s 

我寫在文件中ex.hs並加載它在ghci中 - >

Prelude> import Text.JSON 
Prelude Text.JSON> :load ex 
Main Text.JSON> let aa = "{\"name\": \"some body\", \"age\" : 23, \"address\" : {\"house\" : 285, \"street\" : \"7th Ave.\", \"city\" : \"New York\", \"state\" : \"New York\", \"zip\" : 10001}}" 
...> decode aa :: Result JSValue 

它返回

Ok (JSObject (JSONObject {fromJSObject = [("name",JSString (JSONString {fromJSString = "some body"})),("age",JSRational False (23 % 1)),("address",JSObject (JSONObject {fromJSObject = [("house",JSRational False (285 % 1)),("street",JSString (JSONString {fromJSString = "7th Ave."})),("city",JSString (JSONString {fromJSString = "New York"})),("state",JSString (JSONString {fromJSString = "New York"})),("zip",JSRational False (10001 % 1))]}))]})) 

不用說,它似乎相當冗長(和可怕)。我試過

...> decode aa :: Result Person 

它給了我一個錯誤。我如何去從這個json字符串填充Person數據結構的實例?例如,我應該怎麼做才能的人的狀態的JSON字符串...

回答

22

的問題是,Text.JSON不知道如何JSON數據轉換爲 您Person數據類型。要做到這一點,您需要或者使類型的Person和 實例,或者您可以使用Text.JSON.GenericDeriveDataTypeable擴展名爲您做的工作。

泛型

Text.JSON.Generic方法將根據您的數據類型的 結構看JSON結構。

{-# LANGUAGE DeriveDataTypeable #-} 
import   Text.JSON.Generic 

data Address = Address 
    { house :: Integer 
    , street :: String 
    , city :: String 
    , state :: String 
    , zip :: Integer 
    } deriving (Show, Data, Typeable) 

data Person = Person 
    { name :: String 
    , age  :: Integer 
    , address :: Address 
    } deriving (Show, Data, Typeable) 

aa :: String 
aa = "{\"name\": \"some body\", \"age\" : 23, \"address\" : {\"house\" : 285, \"street\" : \"7th Ave.\", \"city\" : \"New York\", \"state\" : \"New York\", \"zip\" : 10001}}" 

main = print (decodeJSON aa :: Person) 

此方法效果非常好,只要你不介意在你的數據結構中的字段 的名稱匹配您的JSON格式。另外,您不需要編寫像getNamegetAddress, 和getState這樣的函數。您記錄類型中字段的名稱是訪問者 函數。

∀ x. x ⊦ :t house 
house :: Address -> Integer 
∀ x. x ⊦ :t address 
address :: Person -> Address 

JSON實例

或者,你可以走高端路線,並實現自己的 的JSON類的實例。

import   Control.Applicative 
import   Control.Monad 
import   Text.JSON 

data Address = Address 
    { house :: Integer 
    , street :: String 
    , city :: String 
    , state :: String 
    -- Renamed so as not to conflict with zip from Prelude 
    , zipC :: Integer 
    } deriving (Show) 

data Person = Person 
    { name :: String 
    , age  :: Integer 
    , address :: Address 
    } deriving (Show) 

aa :: String 
aa = "{\"name\": \"some body\", \"age\" : 23, \"address\" : {\"house\" : 285, \"street\" : \"7th Ave.\", \"city\" : \"New York\", \"state\" : \"New York\", \"zip\" : 10001}}" 

-- For convenience 
(!) :: (JSON a) => JSObject JSValue -> String -> Result a 
(!) = flip valFromObj 

instance JSON Address where 
    -- Keep the compiler quiet 
    showJSON = undefined 

    readJSON (JSObject obj) = 
     Address  <$> 
     obj ! "house" <*> 
     obj ! "street" <*> 
     obj ! "city" <*> 
     obj ! "state" <*> 
     obj ! "zip" 
    readJSON _ = mzero 

instance JSON Person where 
    -- Keep the compiler quiet 
    showJSON = undefined 

    readJSON (JSObject obj) = 
     Person  <$> 
     obj ! "name" <*> 
     obj ! "age" <*> 
     obj ! "address" 
    readJSON _ = mzero 

main = print (decode aa :: Result Person) 

這利用了以下事實的優點,即Result類型是Applicative容易地 鏈一起查詢在JSObject值。

這是多一點的工作,但它給你的 ,如果你要處理JSON,將導致風格指引 侵犯JSON結構進行更多的控制,由於怪異的字段名。

+0

也許你也應該舉一個例子來創建一個JSON實例,因爲你提到它作爲一種替代方案。 – Wes

+0

@韋斯,你走了。 – sabauma

+0

非常有用的信息。我有個問題。除了'Text.JSON.Generic'(這個包是從哪裏來的?),我還發現https://hackage.haskell.org/package/generic-aeson類似地使用泛型機制來生成Haskell的JSON實例數據。這兩個軟件包有什麼區別? –

5

也許在遊戲中有點晚,但因爲這是谷歌返回的第一頁,我會給它一個去。

Aeson這些日子是事實上的標準,所以這是大家使用的圖書館。 Aeson TH包提供了一些很好的功能,可以爲您的自定義數據類型自動生成必要的功能。

基本上你可以創建對應於json數據的數據類型,然後讓aeson做到這一點。

{-# LANGUAGE OverloadedStrings,TemplateHaskell #-} 
import Data.Aeson 
import Data.Aeson.TH 
import qualified Data.ByteString.Lazy.Char8 as BL 

data Address = Address 
    { house :: Integer 
    , street :: String 
    , city :: String 
    , state :: Maybe String 
    , zip :: Integer 
    } deriving (Show, Eq) 

data Person = Person 
    { name :: String 
    , age  :: Integer 
    , address :: Address 
    } deriving (Show, Eq) 

$(deriveJSON defaultOptions ''Address) 
$(deriveJSON defaultOptions ''Person) 

aa :: BL.ByteString 
aa = "{\"name\": \"some body\", \"age\" : 23, \"address\" : {\"house\" : 285, \"street\" : \"7th Ave.\", \"city\" : \"New York\", \"state\" : \"New York\", \"zip\" : 10001}}" 

main = print (decode aa :: Maybe Person) 

你甚至可以有與Maybe數據類型可選字段。

+0

Aeson必須鏈接比'json'包更多的庫 – dani24