A JSON-LD processing library for Clojure and ClojureScript that provides core JSON-LD operations including expansion, compaction, normalization, and RDF conversion.
- JSON-LD Processing: Expand, compact, flatten*, and normalize JSON-LD documents (*flatten has limited ClojureScript support)
- Cross-platform: Works in both Clojure (JVM) and ClojureScript (JS) environments
- External Context Support: Pre-loaded contexts for common vocabularies (Schema.org, W3C, Fluree, etc.)
- RDF Conversion: Convert between JSON-LD and RDF formats (N-Quads)
- Context Parsing: Parse and manipulate JSON-LD contexts
Add the following dependency to your deps.edn
:
com.fluree/json-ld {:mvn/version "0.1.0"}
The library provides the fluree.json-ld
namespace with JSON-LD operations including:
- Context parsing and manipulation
- IRI expansion and compaction
- Document expansion
- Data normalization
- External context and vocabulary support
Note: Most operations in this library require a parsed context. Parsing contexts once and reusing them makes expansion and compaction operations more efficient, especially when processing multiple documents with the same context.
JSON-LD Keywords: When expanding documents, special JSON-LD keywords (like @id
, @type
, @graph
, @list
, @value
) are converted to Clojure keywords (:id
, :type
, :graph
, :list
, :value
) in the resulting data structure.
Parse and work with JSON-LD contexts:
(require '[fluree.json-ld :as json-ld])
;; Parse a simple context
(def ctx (json-ld/parse-context
{"@context" {"name" "http://schema.org/name"
"age" {"@id" "http://schema.org/age"
"@type" "http://www.w3.org/2001/XMLSchema#integer"}}}))
;; Parse with external contexts
(def ctx (json-ld/parse-context
{"@context" ["https://schema.org"
{"myapp" "https://myapp.com/ns#"}]}))
Expand JSON-LD to its full form:
;; Expand a JSON-LD document
(json-ld/expand
{"@context" {"name" "http://schema.org/name"}
"@id" "http://example.org/person/1"
"name" "John Doe"})
;; => {:id "http://example.org/person/1"
;; :idx []
;; "http://schema.org/name" [{:value "John Doe"
;; :type nil
;; :idx ["name"]}]}
;; The :idx metadata tracks the path in the original document,
;; useful for error reporting. For nested structures:
(json-ld/expand
{"@context" {"knows" "http://schema.org/knows"}
"@id" "http://example.org/person/1"
"knows" {"@id" "http://example.org/person/2"
"knows" {"@id" "http://example.org/person/3"}}})
;; => Nested structure with :idx paths like ["knows"] and ["knows" "knows"]
;; Special JSON-LD terms (@id, @type, @graph, etc.) are converted to keywords
(json-ld/expand
{"@context" {"nick" {"@id" "http://xmlns.com/foaf/0.1/nick"
"@container" "@list"}}
"@id" "http://example.org/people#joebob"
"nick" ["joe" "bob" "jaybee"]})
;; => {:id "http://example.org/people#joebob"
;; :idx []
;; "http://xmlns.com/foaf/0.1/nick"
;; [{:list [{:value "joe" :type nil :idx ["nick" 0]}
;; {:value "bob" :type nil :idx ["nick" 1]}
;; {:value "jaybee" :type nil :idx ["nick" 2]}]}]}
;; @graph returns a vector of expanded nodes
(json-ld/expand {"@graph" [{"@id" "http://example.org/1" "name" "John"}]})
;; => [{:id "http://example.org/1" :idx ["@graph" 0] ...}]
;; Expand a single IRI
(json-ld/expand-iri "schema:name" parsed-context)
;; => "http://schema.org/name"
Compact expanded JSON-LD using a context:
;; Create a compacting function
(def compact-fn (json-ld/compact-fn parsed-context))
(compact-fn "http://schema.org/name")
;; => "schema:name"
;; Track which context terms were used
(def used (atom #{}))
(def compact-fn (json-ld/compact-fn parsed-context used))
(compact-fn "http://schema.org/name")
@used ;; => #{"schema"}
Normalize JSON-LD for consistent comparison and hashing:
(json-ld/normalize-data
{"@context" {"name" "http://schema.org/name"}
"name" "John Doe"
"@id" "http://example.org/person/1"})
;; => Returns normalized JSON string
Get expanded IRI with context settings:
;; Get expansion details including context settings
(json-ld/details "schema:name" parsed-context)
;; => ["http://schema.org/name" {:id "schema:name", ...}]
;; Control vocabulary expansion
(json-ld/details "myapp:userId" parsed-context false)
;; => Expands as @id rather than vocabulary term
;; Check if a document appears to be JSON-LD
(json-ld/json-ld? {"@context" {...} "@id" "..."})
;; => true
(json-ld/json-ld? {"name" "John"})
;; => false
;; Load external IRIs with additional vocabulary information
(json-ld/external-iri "http://schema.org/Person")
;; => Returns IRI information
(json-ld/external-vocab "http://schema.org/name")
;; => Returns vocabulary details including rdfs:subClassOf relationships
The library works identically in ClojureScript:
(ns my-app.core
(:require [fluree.json-ld :as json-ld]))
;; All the same functions work in ClojureScript
(def ctx (json-ld/parse-context {"@context" {...}}))
(def expanded (json-ld/expand my-json-ld-doc ctx))
(def compacted-iri (json-ld/compact "http://schema.org/name" ctx))
The library provides a JavaScript-friendly ESM (ES Module) API that works in both Node.js and browser environments. The JavaScript API automatically handles data conversion between JavaScript objects and ClojureScript data structures.
npm install @fluree/json-ld
// Node.js
import { expand, compact, normalizeData, parseContext, jsonLd } from '@fluree/json-ld';
// Browser
import { expand, compact, normalizeData, parseContext, jsonLd } from './dist/browser/fluree-json-ld.js';
// Works with plain JavaScript objects
const doc = {
"@context": { "name": "http://schema.org/name" },
"@type": "Person",
"name": "John Doe"
};
// JSON-LD detection
const isJsonLd = jsonLd(doc); // true
// Parse context (returns internal format for other functions)
const context = parseContext(doc["@context"]);
// Expand document
const expanded = expand(doc);
// Normalize for hashing/comparison
const normalized = normalizeData(doc); // Returns string
// Full workflow
const parsedContext = parseContext({ "name": "http://schema.org/name" });
const expandedDoc = expand(doc);
const compactedIri = compact(expandedDoc, parsedContext);
expand(document, [context])
- Expands JSON-LD documentscompact(iri, context)
- Compacts expanded IRIsnormalizeData(document, [options])
- Normalizes for hashing/comparisonparseContext(context, [baseContext], [externals])
- Parses JSON-LD contextsexpandIri(compactIri, context, [vocab])
- Expands compact IRIscompactFn(context, [usedAtom])
- Returns compaction functionjsonLd(document)
- Detects if document is JSON-LD
$ make js-package # Build both Node.js and browser ESM modules
$ make node # Build Node.js ESM module only
$ make browser # Build browser ESM module only
The library includes pre-parsed contexts for common vocabularies:
- Schema.org:
https://schema.org
- W3C Credentials:
https://www.w3.org/2018/credentials/v1
- W3C DID:
https://www.w3.org/ns/did/v1
- W3C SHACL:
http://www.w3.org/ns/shacl
- Fluree Ledger:
https://ns.flur.ee/ledger#
- And more...
Access them directly:
;; Load a pre-fetched external context
(json-ld/external-context "https://schema.org")
;; Load vocabulary information for an IRI
(json-ld/external-vocab "http://schema.org/Person")
ClojureScript tests require Node.js and npm. The Makefile will automatically install npm dependencies when running tests.
Run the project's tests:
$ make test # Run all tests (Clojure + JavaScript ESM)
$ make cljtest # Run Clojure tests only
$ make cljstest # Run ClojureScript tests only (auto-installs npm deps)
$ make esm-test # Run JavaScript ESM tests only
$ make esm-test-node # Run Node.js ESM tests only
$ make esm-test-conversion # Run JS<->CLJ data conversion tests
$ make test-ci # Run all tests for CI/CD
$ make test-ci-workflow # Test complete CI workflow locally
The JavaScript tests verify:
- ESM module loading in Node.js and browsers
- All API functions work with JavaScript objects
- Automatic data conversion between JavaScript and ClojureScript
- Complex nested data structures and edge cases
Lint and format code:
$ make lint # Run clj-kondo linter
$ make lint-ci # Run stricter CI linting
$ make fmt # Auto-format code
$ make fmt-check # Check formatting without changing files
Build a deployable jar:
$ make jar
Build JavaScript ESM modules:
$ make js-package # Build both Node.js and browser modules
$ make all # Build both JAR and JavaScript modules
Install locally:
$ make install
Deploy to Clojars (requires CLOJARS_USERNAME
and CLOJARS_PASSWORD
environment variables):
$ make deploy
The project includes comprehensive CI/CD testing via GitHub Actions:
- Clojure Tests: Unit tests for core functionality
- JavaScript ESM Tests: Functionality and data conversion tests for both Node.js and browser environments
- Code Quality: Linting with clj-kondo and code formatting checks
- Cross-platform: Tests run on Ubuntu with Java 17 and Node.js 18
Local CI Testing:
$ ./test-ci-workflow.sh # Simulate complete CI pipeline locally
The CI workflow automatically:
- Installs dependencies (npm, Maven)
- Builds JAR and JavaScript ESM modules
- Runs all test suites
- Validates code quality and formatting
Re-parse all external contexts:
$ make parse-all-contexts
Copyright © 2021-2025 Fluree PBC
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.