
Parses and dereferences JSON Schema $ref pointers across files and URLs, merges allOf, preserves source locations, tracks original refs, handles circular references and authentication.
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:
RefParser as the primary API for Kotlin and Node.js integrationsJavaRefParser as the blocking JVM facade for Java and Kotlin/JVM callers$RefParser and $Refs usersThis is the library to choose for new JVM and Node.js integrations.
json-schema-ref-parser-kmp.json-schema-ref-parser-jvm users can migrate incrementally using the JVM compatibility layer.$RefParser API remains available on the JVM for compatibility, but it is not the primary API going forward.Current status:
RefParser.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.
$ref pointers into a plain object tree$ref pointers to the same target resolve to the same object instanceallOf arrays into a single objectjson-schema-ref-parser-jvm usersval doc = RefParser("path/to/openapi.yml")
.dereference()
.mergeAllOf()
.getParsedDocument()
val schema: Map<String, Any?> = doc.schemaFor 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();import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";
const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
console.log(doc.schema);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>dependencies {
implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp:<version>")
}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.
RefParser is the main suspend-first API for Kotlin and Node.js integrations. Blocking callers on the JVM should use JavaRefParser.
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()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()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) // trueval doc = RefParser(
uri = "path/to/schema.yml",
options = RefParserOptions(onMissing = OnMissing.SKIP),
).dereference().getParsedDocument()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()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()val doc = RefParser("classpath:/schemas/openapi.yml")
.dereference()
.getParsedDocument()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}")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"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()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();The JVM module still ships the legacy compatibility API for existing json-schema-ref-parser-jvm users:
$RefParser$Refs$Ref$RefParserOptionsUse 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();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");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[""]);The generic core module contains a non-mutating, graph-safe JSON Merge Patch utility:
import io.zenwave360.jsonrefparser.merge.JsonMergePatch
import io.zenwave360.jsonrefparser.merge.jsonMergePatch
val effective = JsonMergePatch.apply(target, patch)
val equivalent = jsonMergePatch(target, patch)It implements RFC 7396 semantics: object members merge recursively, null removes an object member, and arrays/scalars replace atomically. The result does not alias mutable maps or lists from either input. Circular and shared parser graphs terminate safely.
This is the same utility that powers trait composition in asyncapi-parser-kmp.
Maven Central publishing is handled by GitHub Actions. Repository prerequisites and the release sequence are documented in RELEASING.md.
JSON Schema $Ref Parser KMP is free and open-source under the MIT license.
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:
RefParser as the primary API for Kotlin and Node.js integrationsJavaRefParser as the blocking JVM facade for Java and Kotlin/JVM callers$RefParser and $Refs usersThis is the library to choose for new JVM and Node.js integrations.
json-schema-ref-parser-kmp.json-schema-ref-parser-jvm users can migrate incrementally using the JVM compatibility layer.$RefParser API remains available on the JVM for compatibility, but it is not the primary API going forward.Current status:
RefParser.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.
$ref pointers into a plain object tree$ref pointers to the same target resolve to the same object instanceallOf arrays into a single objectjson-schema-ref-parser-jvm usersval doc = RefParser("path/to/openapi.yml")
.dereference()
.mergeAllOf()
.getParsedDocument()
val schema: Map<String, Any?> = doc.schemaFor 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();import { dereferenceSchema } from "@zenwave360/json-schema-ref-parser-kmp";
const doc = await dereferenceSchema("file:///workspace/openapi.yml", true);
console.log(doc.schema);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>dependencies {
implementation("io.zenwave360.jsonrefparser:json-schema-ref-parser-kmp:<version>")
}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.
RefParser is the main suspend-first API for Kotlin and Node.js integrations. Blocking callers on the JVM should use JavaRefParser.
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()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()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) // trueval doc = RefParser(
uri = "path/to/schema.yml",
options = RefParserOptions(onMissing = OnMissing.SKIP),
).dereference().getParsedDocument()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()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()val doc = RefParser("classpath:/schemas/openapi.yml")
.dereference()
.getParsedDocument()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}")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"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()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();The JVM module still ships the legacy compatibility API for existing json-schema-ref-parser-jvm users:
$RefParser$Refs$Ref$RefParserOptionsUse 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();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");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[""]);The generic core module contains a non-mutating, graph-safe JSON Merge Patch utility:
import io.zenwave360.jsonrefparser.merge.JsonMergePatch
import io.zenwave360.jsonrefparser.merge.jsonMergePatch
val effective = JsonMergePatch.apply(target, patch)
val equivalent = jsonMergePatch(target, patch)It implements RFC 7396 semantics: object members merge recursively, null removes an object member, and arrays/scalars replace atomically. The result does not alias mutable maps or lists from either input. Circular and shared parser graphs terminate safely.
This is the same utility that powers trait composition in asyncapi-parser-kmp.
Maven Central publishing is handled by GitHub Actions. Repository prerequisites and the release sequence are documented in RELEASING.md.
JSON Schema $Ref Parser KMP is free and open-source under the MIT license.