    {-# LANGUAGE DeriveDataTypeable #-}
    -- | Basic support for working with JSON values.

    module Text.JSON.Types (

        -- * JSON Types
        JSValue(..)

        -- * Wrapper Types
      , JSString({-fromJSString-}..)
      , toJSString

      , JSObject({-fromJSObject-}..)
      , toJSObject

      , get_field
      , set_field

      ) where

    import Data.Typeable ( Typeable )
    import Data.String(IsString(..))

    --
    -- | JSON values
    --
    -- The type to which we encode Haskell values. There's a set
    -- of primitives, and a couple of heterogenous collection types.
    --
    -- Objects:
    --
    -- An object structure is represented as a pair of curly brackets
    -- surrounding zero or more name\/value pairs (or members).  A name is a
    -- string.  A single colon comes after each name, separating the name
    -- from the value.  A single comma separates a value from a
    -- following name.
    --
    -- Arrays:
    --
    -- An array structure is represented as square brackets surrounding
    -- zero or more values (or elements).  Elements are separated by commas.
    --
    -- Only valid JSON can be constructed this way
    --
    data JSValue
        = JSNull
        | JSBool     !Bool
        | JSRational Bool{-as Float?-} !Rational
        | JSString   JSString
        | JSArray    [JSValue]
        | JSObject   (JSObject JSValue)
        deriving (Show, Read, Eq, Ord, Typeable)

    -- | Strings can be represented a little more efficiently in JSON
    newtype JSString   = JSONString { fromJSString :: String }
        deriving (Eq, Ord, Show, Read, Typeable)

    -- | Turn a Haskell string into a JSON string.
    toJSString :: String -> JSString
    toJSString = JSONString
      -- Note: we don't encode the string yet, that's done when serializing.

    instance IsString JSString where
      fromString = toJSString

    instance IsString JSValue where
      fromString = JSString . fromString

    -- | As can association lists
    newtype JSObject e = JSONObject { fromJSObject :: [(String, e)] }
        deriving (Eq, Ord, Show, Read, Typeable )

    -- | Make JSON object out of an association list.
    toJSObject :: [(String,a)] -> JSObject a
    toJSObject = JSONObject

    -- | Get the value of a field, if it exist.
    get_field :: JSObject a -> String -> Maybe a
    get_field (JSONObject xs) x = lookup x xs

    -- | Set the value of a field.  Previous values are overwritten.
    set_field :: JSObject a -> String -> a -> JSObject a
    set_field (JSONObject xs) k v = JSONObject ((k,v) : filter ((/= k).fst) xs)