miso

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 "{ \"userName\": \"Ada\", \"userAge\": 36 }" update view
  where
    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" <> 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

Coming soon — an aeson polyfill 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 — will work with miso unchanged, with no bridge package and no import churn: keep importing Miso.JSON and flip the flag.