Platform

JSON

Miso.JSON is a microaeson-inspired JSON library specialised to MisoString. On the JS / WASM backends it delegates encoding and decoding to the JavaScript runtime (JSON.stringify / JSON.parse) for performance; on the server (ssr) it uses a pure Haskell implementation. It is used internally by Miso.Event.Decoder, Miso.Fetch and Miso.WebSocket.

Value

data Value
  = Number Double
  | Bool   Bool
  | String MisoString
  | Array  [Value]
  | Object Object
  | Null

Encoding and decoding

encode value
-- uses the JS runtime on the client,
-- pure on the server

encodePure value
-- always the pure Haskell implementation

decode s            :: Maybe a
eitherDecode s      :: Either MisoString a

ToJSON / FromJSON

Derive instances via GHC.Generics:

{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Miso.JSON

data User = User
  { name :: MisoString
  , age  :: Int
  } deriving (Generic)

instance ToJSON User
instance FromJSON User

Use genericToJSON / genericParseJSON with Options to customise field and constructor names; camelTo2 converts camelCase to snake_case:

instance ToJSON User where
  toJSON = genericToJSON defaultOptions
    { fieldLabelModifier = camelTo2 '_' }

Building and parsing objects

-- Build
object
  [ "name" .= ms "Alice"
  , "age" .= (30 :: Int)
  ]

-- Parse (inside a withObject
-- callback or event decoder)
withObject "User" $ \o -> User
  <$> o .:  "name"
      -- required field
  <*> o .:  "age"

o .:? "nickname"
-- optional field → Maybe a

o .:! "nickname"
-- optional field, explicit null → Maybe a

p .!= "anon"
-- default for a Maybe parser

Pretty printing

encodePretty value
-- indented with defConfig (2-space indent)

encodePretty' config value
-- custom Config

Try it

Round-tripping a record with generic instanceslive
✓ User {userName = "Ada", userAge = 36}
{"userAge":36,"userName":"Ada"}
data User = User
  { userName :: MisoString
  , userAge  :: Int
  } deriving (Show, Eq, Generic, ToJSON, FromJSON)
    -- generic instances from Miso.JSON

data JsonAction = Edit MisoString

jsonRoundTrip
  :: Component ctx () MisoString JsonAction
jsonRoundTrip = component initial update view
  where
    initial = "{ \"userName\": \"Ada\", \"userAge\": 36 }"

    update (Edit s) = this .= s

    view input =
      H.div_ []
        [ H.textarea_
            [ HP.value_ input
            , HE.onInput Edit
            , textProp "rows" "3"
            ]
        , case eitherDecode input :: Either MisoString User of
            Left err ->
              H.p_ [ HP.class_ "muted" ] [ "✗ ", text err ]
            Right user ->
              H.pre_ []
                [ "✓ "
                , text (ms (show user))
                , "\n"
                , text (encode user)
                ]
        ]

miso-aeson

Prefer aeson? The miso-aeson package bridges aeson's ToJSON / FromJSON instances with miso's event decoder and fetch API, so existing instances work without rewriting.

aeson polyfill

An aeson polyfill lives behind an aeson cabal flag (-faeson): building miso with it defines every operator and class exported by Miso.JSON (ToJSON, FromJSON, .:, .=, withObject, encode, decode, …) in terms of Data.Aeson. Existing aeson code — and its instances — works with miso unchanged, with no bridge package and no import churn: keep importing Miso.JSON and flip the flag.