2013-07-24 30 views
10

Haskell'de JSON verilerini ayrıştırmaya çalışıyorum. Çok sayıda web sitesine girdiğimden bu, ulaşabildiğim en uzundu.json ayrıştırma haskell

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 

Bir dosya ex.hs ve GHCi yükleyecek olduğunu yazma ->

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 

Bu oldukça ayrıntılı görüküyor

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))]}))]})) 

Tabii döndürür (ve korkutucu).

...> decode aa :: Result Person 

'u yapmayı denedim ve bana bir hata verdi. Bu json dizisinden Kişi veri yapısının bir örneğini doldurmaya nasıl giderim? Mesela ben

cevap

22

sorun Text.JSON sizin Person veri türüne JSON verileri dönüştürmek için nasıl bilmiyor ki ... JSON dizesinde kişinin durumunu elde etmek için ne yapmalıyım. Bunu yapmak için, ve yazım tipinin örneğini yapmanız gerekir, ya da sizin işinizi yapmak için Text.JSON.Generic ve ve DeriveDataTypeable uzantısını kullanabilirsiniz.

Jenerik

Text.JSON.Generic yöntem veri türünde yapısına göre JSON yapısını okuyacaktır.

{-# 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) 

Bu yöntem gerçekten iyi sürece sizin JSON biçimine veri yapısındaki alanların isimlerini eşleşen umursamıyorum olarak çalışmaktadır.

bir kenara olarak, getName, getAddress, ve getState gibi işlevleri yazmak için gerek yoktur. Kayıt türünüzdeki alanın isimleri, fonksiyonlarının accesor'udur.

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

JSON Örneği

Alternatif olarak, yüksek yol almak ve JSON sınıf kendi örneğini uygulamak.

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) 

Bu Result tip bir Applicative olmasından yararlanır kolayca zincir birlikte JSObject değerine sorgular.

Bu biraz daha fazla iş, ama nedeniyle garip alan adları için size JSON stil kılavuz ihlalleri neden olacaktır JSON uğraşmak zorunda olmadığını yapısı daha fazla kontrol sağlar.

+0

Belki de alternatif olarak bahsettiğinizden beri bir JSON örneğini oluşturma örneği vermelisiniz. – Wes

+0

@Wes, işte gidiyorsunuz. – sabauma

+0

Çok faydalı bilgiler. Bir sorum var. Text.JSON.Generic' (hangi paketden geliyor?) Dışında, aynı şekilde Gencelics makinelerini kullanan ve benzer şekilde Haskell'in JSON örneklerini yapmak için https://hackage.haskell.org/package/generic-aeson'u da buldum. veri. Bu iki paket arasındaki farklar nelerdir? –

5

Belki oyunda biraz geç kalmış, ancak bu ilk sayfa olduğu için google geri dönecektir.Bu kütüphane herkes kullanır yani

Aeson bugünlerde defacto standarttır. Aeson TH paketi, özel veri türleriniz için gerekli işlevleri otomatik olarak oluşturmak için bazı güzel işlevler sunar.

Temelde json verilere karşılık gelen veri türleri oluşturmak ve sonra büyü yapmak aeson edelim.

{-# 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) 

Hatta Maybe veri türü ile isteğe bağlı alanları olabilir.

+0

Aeson 'json' paketinden çok daha fazla kütüphane bağlamalıdır – dani24