анализ json в haskell
Я пытаюсь проанализировать данные JSON в 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
Я пишу это в файле 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
и это дало мне ошибку. Как я могу заполнить экземпляр Person datastructure из этого json струна? Например, что я должен сделать, чтобы получить состояние человека в строке JSON...
2 ответов
проблема в том, что Text.JSON
не знает, как конвертировать JSON
данные
ваш Person
тип данных. Для этого нужно либо сделать Person
и
пример JSON
typeclass, или вы можете использовать Text.JSON.Generic
и
DeriveDataTypeable
расширение, чтобы сделать работу за вас.
дженериков
на 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)
этот метод работает очень хорошо, пока вы не возражаете сопоставление имен полей в структуре данных в свой .
в стороне, вам не нужно писать такие функции, как getName
, getAddress
,
и getState
. Имена полей в типе записи-accesor
функции.
∀ x. x ⊦ :t house
house :: Address -> Integer
∀ x. x ⊦ :t address
address :: Person -> Address
экземпляр JSON
кроме того, вы можете пойти по большой дороге и реализовать свой собственный экземпляр
the 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
значение.
это немного больше работы, но это дает вам больше контроля структуры
the JSON
если вам придется иметь дело с JSON
это вызовет руководство по стилю
нарушения из-за странных названий полей.
может быть, немного поздно в игре, но так как это первая страница Google возвращает я дам ему идти.
Aeson является стандартом defacto в эти дни, так что это библиотека, которую все используют. The Эсон 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
тип данных.