Accepted (Implemented)
The project initially used clojure.data.json for JSON parsing and generation. As the project evolved, several factors motivated a migration to the cheshire library:
Migrate from clojure.data.json to cheshire as the JSON library, with the following implementation strategy:
Create a dedicated json component (components/json/) that encapsulates all JSON operations behind a simple API:
(require '[mcp-clj.json :as json])
(json/parse json-string) ; Parse JSON to EDN
(json/write edn-data) ; Convert EDN to JSON
This centralization provides:
Cheshire has two behavioral differences from the expected JSON parsing behavior that required a compatibility layer:
Issue 1: Integer vs Long
java.lang.Integerjava.lang.LongConcurrentHashMap lookup failures when Integer keys don't match Long keysIssue 2: LazySeq vs Vector
clojure.lang.LazySeqclojure.lang.PersistentVectorvector? checks and indexed access patternsSolution: normalize-parsed-json
Implemented a private normalize-parsed-json function that walks the parsed data structure and:
Integer instances to Long for Java interop compatibilityThis normalization is applied transparently in json/parse, maintaining API compatibility while ensuring consistent behavior across the codebase.
| clojure.data.json | Cheshire (via mcp-clj.json) | Notes |
|---|---|---|
(json/read-str s :key-fn keyword) | (json/parse s) | Auto-converts keys to keywords |
(json/write-str data) | (json/write data) | Auto-converts keyword keys to strings |
(json/write-str data {:key-fn name}) | (json/write data) | Same behavior, built into write |
The migration was completed through a series of commits:
47c7eb0 - Migrate json_protocol.clj to cheshire API1c3c2d0 - Migrate stdio JSON I/O from clojure.data.json to cheshire36d9efb - Migrate HTTP modules from clojure.data.json to cheshire2c0ff84 - Add cheshire compatibility layer for JSON parsing (first normalization attempt)1395687 - Create json component to centralize JSON handlingd6780b9 - Normalize JSON-RPC request IDs to Long for HashMap lookups1fa8b5c - Complete JSON component API migration and add normalization (final solution)The normalization layer was essential to fix batch request test failures caused by type inconsistencies.
mcp-clj.json component provides single point of controlRejected: Missing Babashka support and slower performance outweighed migration effort
Rejected: Not built into Babashka. While faster, cheshire's performance is sufficient and Babashka compatibility is more important
Rejected: Would require changes throughout the codebase and create ongoing maintenance burden. Centralized normalization is more maintainable
Tried: Initially attempted in commit 2c0ff84 but caused test failures. The type differences were too pervasive to fix piecemeal
components/json/src/mcp_clj/json.cljjson componentCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |