json parsing in haskell

后端 未结 2 1108
心在旅途
心在旅途 2020-12-30 04:15

I\'m trying to parse JSON data in haskell. Having gone through a slew of websites, this is the furthest I have been able to get to.

data Address = Address {          


        
相关标签:
2条回答
  • 2020-12-30 04:37

    Maybe a bit late in the game, but since this is the first page google returns I'll give it a go.

    Aeson is the defacto standard these days so that's the library everybody uses. The Aeson TH package offers some nice functionality for automatically generating the necessary functions for your custom data types.

    Basically you create your data types that correspond to the json data and then let aeson do the magic.

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

    You can even have optional fields with the Maybe datatype.

    0 讨论(0)
  • 2020-12-30 04:45

    The problem is that Text.JSON does not know how to convert JSON data to your Person data type. To do this, you need to either make Person and instance of the JSON typeclass, or your can use Text.JSON.Generic and the DeriveDataTypeable extension to do the work for you.

    Generics

    The Text.JSON.Generic method will read the JSON structure based on the structure of your data type.

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

    This method works really well as long as you don't mind matching the names of the fields in your data structure to your JSON format.

    As an aside, you don't need to write functions like getName, getAddress, and getState. The names of the field in your record type are accesor functions.

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

    JSON Instance

    Alternatively, you could take the high road and implement your own instance of the JSON class.

    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)
    

    This takes advantage of the fact that the Result type is an Applicative to easily chain together queries on the JSObject value.

    This is a little more work, but it gives you more control of the structure of the JSON if you have to deal with JSON that will cause style guideline violations due to weird field names.

    0 讨论(0)
提交回复
热议问题