JSON Schema $Ref Parser for JVM and Node.js
Parse, resolve, and dereference JSON Schema $ref pointers on the JVM and Node.js.
This library is the Kotlin Multiplatform evolution of json-schema-ref-parser-jvm. It provides:
RefParseras the primary API for Kotlin and Node.js integrationsJavaRefParseras the blocking JVM facade for Java and Kotlin/JVM callers- a JVM compatibility layer for existing
$RefParserand$Refsusers - JS exports for Node.js runtimes
Project Status
This is the library to choose for new JVM and Node.js integrations.
- New users should start with
json-schema-ref-parser-kmp. - Existing
json-schema-ref-parser-jvmusers can migrate incrementally using the JVM compatibility layer. - The
$RefParserAPI remains available on the JVM for compatibility, but it is not the primary API going forward.
Current status:
- The Kotlin Multiplatform core is working and is intended to match the JVM implementation behavior.
- The main API has been reshaped around
RefParser. - Circular reference handling has been reworked in the new implementation and still benefits from broader real-world validation.
The Problem
If you work with JSON Schema, OpenAPI, or AsyncAPI specifications, you know the pain: schemas spread across multiple files, $ref pointers everywhere, and yet another ad hoc parser seems inevitable.
This library handles all of that for you. It is a full JSON Reference and JSON Pointer implementation that crawls even complex schemas and returns a simple object tree with source locations and resolved ref tracking.
Quick Start
Kotlin with RefParser
val doc = RefParser("path/to/openapi.yml")
.dereference()
.mergeAllOf()
.getParsedDocument()
val schema: Map<String, Any?> = doc.schema
Java with JavaRefParser
For blocking callers on the JVM, use the JavaRefParser facade built on top of RefParser.
import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.model.ParsedDocument;
import java.io.File;
import java.util.Map;
File file = new File("src/main/resources/openapi.yml");
ParsedDocument doc = JavaRefParser.from(file)
.dereference()
.mergeAllOf()
.getParsedDocument();
Map<String, Object> schema = (Map<String, Object>) doc.getSchema();
Node.js
import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";
const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
console.log(doc.schema);
Installation
Java and Kotlin on the JVM
Gradle:
dependencies {
implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp-jvm:<version>")
}
Maven:
<dependency>
<groupId>io.zenwave360.jsonrefparser</groupId>
<artifactId>json-schema-ref-parser-kmp-jvm</artifactId>
<version>${json-schema-ref-parser-kmp-jvm.version}</version>
</dependency>
Kotlin Multiplatform
dependencies {
implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp:<version>")
}
Node.js
The Node.js API is available from the JS target exports:
parseSchemaText(input, baseUri?)dereferenceSchema(uri, mergeAllOf?)dereferenceSchemaText(input, baseUri?, mergeAllOf?)
The npm package name is @zenwave360/json-schema-ref-parser-kmp. npm publishing is not enabled yet, so this repository currently publishes Maven Central artifacts only.
Usage
RefParser Primary API
RefParser is the main suspend-first API for Kotlin and Node.js integrations. Blocking callers on the JVM should use JavaRefParser.
Dereference
Resolves all $ref pointers and replaces them inline, including references to external files and remote URLs.
val doc = RefParser("path/to/schema.yml")
.dereference()
.getParsedDocument()
Merge allOf
After dereferencing, merges every allOf array into its parent object, combining fields such as properties and required.
val doc = RefParser("path/to/schema.yml")
.dereference()
.mergeAllOf()
.getParsedDocument()
Circular references
By default, circular references are resolved by preserving object identity. You can also skip them or fail fast.
val doc = RefParser(
uri = "path/to/schema.yml",
options = RefParserOptions(onCircular = OnCircular.SKIP),
).dereference().getParsedDocument()
println(doc.hasCircularRefs) // true
Missing references
val doc = RefParser(
uri = "path/to/schema.yml",
options = RefParserOptions(onMissing = OnMissing.SKIP),
).dereference().getParsedDocument()
Authentication for remote files
val doc = RefParser(
uri = "path/to/schema.yml",
auth = listOf(
AuthenticationValue(
key = "Authorization",
value = "Bearer <token>",
urlMatcher = { url -> url.contains("api.example.com") },
)
),
).dereference().getParsedDocument()
Loader configuration
Replace the loader chain completely:
val doc = RefParser("classpath:/schemas/openapi.yml")
.withLoaders(
ClasspathLoader(pluginClassLoader),
FileLoader(),
HttpLoader(),
)
.dereference()
.getParsedDocument()
Patch only the default chain, replacing matching loader types and preserving the rest:
val doc = RefParser("classpath:/schemas/openapi.yml")
.withDefaultLoaders(
ClasspathLoader(pluginClassLoader),
)
.dereference()
.getParsedDocument()
Classpath loading on the JVM
val doc = RefParser("classpath:/schemas/openapi.yml")
.dereference()
.getParsedDocument()
Source locations
Every node in the parsed document carries its original file and line and column range, even after dereferencing across multiple files.
val doc = RefParser("path/to/schema.yml")
.dereference()
.getParsedDocument()
val location = doc.locations["/info"]
println("${location?.file}:${location?.line}:${location?.column}")
Original ref tracking
After dereferencing, you can look up which $ref string a given object came from.
val doc = RefParser("path/to/schema.yml")
.dereference()
.getParsedDocument()
val ref = doc.getOriginalRef(someSchemaObject)
println(ref?.refString) // e.g. "#/components/schemas/Pet"
Loading from a string
Useful in tests or when you already have the document text in memory.
val yaml = """
type: object
properties:
name:
type: string
""".trimIndent()
val doc = RefParser.fromText(yaml).dereference().getParsedDocument()
Java on the JVM
Blocking JVM callers should use the JVM facade:
import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.model.OnCircular;
import io.zenwave360.jsonrefparser.model.OnMissing;
import io.zenwave360.jsonrefparser.model.ParsedDocument;
import io.zenwave360.jsonrefparser.model.RefParserOptions;
import java.io.File;
File file = new File("src/main/resources/openapi.yml");
ParsedDocument doc = JavaRefParser.from(file)
.withOptions(new RefParserOptions(OnCircular.SKIP, OnMissing.FAIL))
.dereference()
.mergeAllOf()
.getParsedDocument();
System.out.println(doc.getSchema());
Patch only the default classpath loader while keeping the default file and HTTP loaders:
import io.zenwave360.jsonrefparser.JavaRefParser;
import java.net.URI;
var doc = JavaRefParser.from(URI.create("classpath:catalog/service/asyncapi.yml"))
.withResourceClassLoader(pluginClassLoader)
.dereference()
.getParsedDocument();
Patch the default loader chain explicitly:
import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.io.ClasspathLoader;
import java.util.Arrays;
var doc = JavaRefParser.from("classpath:catalog/service/asyncapi.yml")
.withDefaultLoaders(Arrays.asList(
new ClasspathLoader(pluginClassLoader)
))
.dereference()
.getParsedDocument();
Replace the loader chain completely:
import io.zenwave360.jsonrefparser.JavaRefParser;
import io.zenwave360.jsonrefparser.io.ClasspathLoader;
import io.zenwave360.jsonrefparser.io.FileLoader;
import io.zenwave360.jsonrefparser.io.HttpLoader;
import java.util.Arrays;
var doc = JavaRefParser.from("classpath:catalog/service/asyncapi.yml")
.withLoaders(Arrays.asList(
new ClasspathLoader(pluginClassLoader),
new FileLoader(),
new HttpLoader()
))
.dereference()
.getParsedDocument();
$Ref Compatibility API on the JVM
The JVM module still ships the legacy compatibility API for existing json-schema-ref-parser-jvm users:
$RefParser$Refs$Ref$RefParserOptions
Use this when you want a low-friction migration path from the old JVM library. For new JVM code, prefer JavaRefParser.
import static io.zenwave360.jsonrefparser.$RefParserOptions.OnCircular.SKIP;
import io.zenwave360.jsonrefparser.$RefParser;
import io.zenwave360.jsonrefparser.$RefParserOptions;
import io.zenwave360.jsonrefparser.$Refs;
import java.io.File;
File file = new File("src/main/resources/openapi.yml");
$Refs refs = new $RefParser(file)
.withOptions(new $RefParserOptions().withOnCircular(SKIP))
.dereference()
.mergeAllOf()
.getRefs();
Object schema = refs.schema();
Source locations with $Refs
import io.zenwave360.jsonrefparser.$RefParser;
import java.io.File;
File file = new File("src/main/resources/openapi.yml");
var range = new $RefParser(file)
.parse()
.getRefs()
.getJsonLocationRange("$.info");
Node.js
The JS target exports plain object APIs designed for ESM runtimes in Node.js.
Dereference a file:
import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";
const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
console.log(doc.hasCircularRefs);
console.log(doc.locations["/info"]);
Dereference in-memory text:
import { dereferenceSchemaText } from "@zenwave360/json-schema-ref-parser-kmp";
const yaml = `
type: object
properties:
pet:
$ref: "#/definitions/Pet"
definitions:
Pet:
type: object
properties:
name:
type: string
`;
const doc = await dereferenceSchemaText(yaml, "memory://pet.yml", true);
console.log(doc.schema);
Parse without dereferencing:
import { parseSchemaText } from "@zenwave360/json-schema-ref-parser-kmp";
const doc = parseSchemaText("{\"type\":\"object\"}", "memory://schema.json");
console.log(doc.locations[""]);
Features
- Parses JSON, YAML, and Avro schemas, or any mix of them
- Dereferences
$refpointers into a plain object tree - Cross-file references: local files, remote URLs, and classpath resources on the JVM
- Object identity: two
$refpointers to the same target resolve to the same object instance - Source locations for every parsed node
- Original ref tracking for resolved objects
- Merges
allOfarrays into a single object - Authentication headers and query parameters for remote loading
- Circular reference detection with resolve, skip, and fail modes
- Missing reference handling with skip and fail modes
- JVM and Node.js support through Kotlin Multiplatform
- JVM compatibility layer for existing
json-schema-ref-parser-jvmusers
Release
Maven Central publishing is handled by GitHub Actions. Repository prerequisites and the release sequence are documented in RELEASING.md.
License
JSON Schema $Ref Parser KMP is free and open-source under the MIT license.