Agent SkillsAgent Skills
aresbit

jsont

@aresbit/jsont
aresbit
45
5 forks
Updated 4/13/2026
View on GitHub

JSON type-safe encoding and decoding using the OCaml jsont library. Use when Claude needs to: define typed JSON codecs for OCaml record types, parse JSON strings to OCaml values, or serialize OCaml values to JSON, or work with nested JSON structures

Installation

$npx agent-skills-cli install @aresbit/jsont
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Repositoryaresbit/MateBot
Pathskills/ocaml/jsont/SKILL.md
Branchmaster
Scoped Name@aresbit/jsont

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

npx agent-skills-cli list

Skill Instructions


name: jsont description: "JSON type-safe encoding and decoding using the OCaml jsont library. Use when Claude needs to: define typed JSON codecs for OCaml record types, parse JSON strings to OCaml values, or serialize OCaml values to JSON, or work with nested JSON structures" license: ISC

Jsont JSON Encoding/Decoding

Dependencies

(libraries jsont jsont.bytesrw)

Core Patterns

Simple Object Codec

Map a JSON object to an OCaml record using Jsont.Object.map with mem for required fields:

type header = {
  message_id : string;
  method_ : string;
  timestamp : int;
}

let header_codec =
  Jsont.Object.map ~kind:"header"
    (fun message_id method_ timestamp -> { message_id; method_; timestamp })
  |> Jsont.Object.mem "messageId" Jsont.string ~enc:(fun h -> h.message_id)
  |> Jsont.Object.mem "method" Jsont.string ~enc:(fun h -> h.method_)
  |> Jsont.Object.mem "timestamp" Jsont.int ~enc:(fun h -> h.timestamp)
  |> Jsont.Object.finish

Optional Fields

Use opt_mem for optional JSON fields. The constructor receives 'a option:

type config = {
  name : string;
  timeout : int;  (* default if missing *)
}

let config_codec =
  Jsont.Object.map ~kind:"config"
    (fun name timeout_opt ->
      { name; timeout = Option.value ~default:30 timeout_opt })
  |> Jsont.Object.mem "name" Jsont.string ~enc:(fun c -> c.name)
  |> Jsont.Object.opt_mem "timeout" Jsont.int ~enc:(fun c -> Some c.timeout)
  |> Jsont.Object.finish

Skip Unknown Fields

Use skip_unknown before finish to ignore extra JSON fields (tolerant parsing):

let tolerant_codec =
  Jsont.Object.map ~kind:"data" (fun id -> { id })
  |> Jsont.Object.mem "id" Jsont.string ~enc:(fun d -> d.id)
  |> Jsont.Object.skip_unknown  (* ignore extra fields *)
  |> Jsont.Object.finish

Nested Objects

Compose codecs for nested structures:

type request = { header : header; payload : payload }

let request_codec payload_codec =
  Jsont.Object.map ~kind:"request" (fun header payload -> { header; payload })
  |> Jsont.Object.mem "header" header_codec ~enc:(fun r -> r.header)
  |> Jsont.Object.mem "payload" payload_codec ~enc:(fun r -> r.payload)
  |> Jsont.Object.finish

Lists

Use Jsont.list for JSON arrays:

type response = { items : item list }

let response_codec =
  Jsont.Object.map ~kind:"response" (fun items -> { items })
  |> Jsont.Object.mem "items" (Jsont.list item_codec) ~enc:(fun r -> r.items)
  |> Jsont.Object.finish

String Maps

Use Jsont.Object.as_string_map for objects with dynamic keys:

module String_map = Map.Make(String)

(* JSON: {"key1": "value1", "key2": "value2"} *)
let string_map_codec = Jsont.Object.as_string_map Jsont.string

(* JSON: {"group1": [...], "group2": [...]} *)
let groups_codec = Jsont.Object.as_string_map (Jsont.list item_codec)

Empty Object

For payloads that don't carry data:

let empty_payload_codec : unit Jsont.t =
  Jsont.Object.map ~kind:"empty" ()
  |> Jsont.Object.skip_unknown
  |> Jsont.Object.finish

Custom Value Mapping

Use Jsont.map to transform between types:

type device_type = Sonos | Meross | Other

let device_from_string =
  Jsont.map ~kind:"device_type"
    ~dec:(function "sonos" -> Sonos | "meross" -> Meross | _ -> Other)
    ~enc:(function Sonos -> "sonos" | Meross -> "meross" | Other -> "other")
    Jsont.string

Polymorphic Decoding with any

Handle multiple JSON shapes for backwards compatibility:

(* Device can be string (old format) or object (new format) *)
let device_compat_codec =
  Jsont.any ~kind:"device"
    ~dec_string:device_from_string_codec  (* handles "192.168.1.1" *)
    ~dec_object:device_object_codec       (* handles {"ip": "...", "type": "..."} *)
    ~enc:(fun _ -> device_object_codec)   (* always encode as object *)
    ()

Null Values

Use Jsont.null for endpoints returning null:

(* For DELETE endpoints that return null on success *)
match delete http ~sw token endpoint (Jsont.null ()) with
| Ok () -> ...

Generic JSON

Use Jsont.json to preserve arbitrary JSON:

type characteristic = {
  iid : int;
  value : Jsont.json option;  (* preserve any JSON value *)
}

let char_codec =
  Jsont.Object.map ~kind:"char" (fun iid value -> { iid; value })
  |> Jsont.Object.mem "iid" Jsont.int ~enc:(fun c -> c.iid)
  |> Jsont.Object.opt_mem "value" Jsont.json ~enc:(fun c -> c.value)
  |> Jsont.Object.finish

Encoding and Decoding

Use Jsont_bytesrw for string-based encoding/decoding:

(* Decode JSON string to OCaml value *)
let decode codec s = Jsont_bytesrw.decode_string codec s
(* Returns: ('a, Jsont.Error.t) result *)

(* Encode OCaml value to JSON string *)
let encode codec v =
  match Jsont_bytesrw.encode_string codec v with
  | Ok s -> s
  | Error _ -> "{}"  (* fallback for encoding errors *)

(* Usage *)
match Jsont_bytesrw.decode_string config_codec json_string with
| Ok config -> (* use config *)
| Error e -> (* handle error *)

match Jsont_bytesrw.encode_string config_codec config with
| Ok json_str -> (* send json_str *)
| Error _ -> (* handle error *)

Common Helpers

Define module-level helpers for cleaner code:

let decode codec s = Jsont_bytesrw.decode_string codec s

let encode codec v =
  match Jsont_bytesrw.encode_string codec v with
  | Ok s -> s
  | Error _ -> ""

Base Types Reference

OCaml TypeJsont CodecJSON Type
stringJsont.stringstring
intJsont.intnumber
floatJsont.numbernumber
boolJsont.boolboolean
'a listJsont.list codecarray
'a optionJsont.option codecvalue or null
unitJsont.null ()null
genericJsont.jsonany JSON

Best Practices

  1. Always use ~kind: Provide descriptive kind names for better error messages
  2. Use skip_unknown for external APIs: Be tolerant of extra fields from third-party services
  3. Prefer opt_mem with defaults: Handle missing fields gracefully with Option.value ~default:
  4. Compose small codecs: Build complex structures from simple, reusable codecs
  5. Define helper functions: Create decode/encode helpers at module level for cleaner usage
jsont by aresbit | Agent Skills