Skip to content

Latest commit

History

History

README.md

JSON Structure SDK for Java

A Java SDK for working with JSON Structure schemas, providing:

  • Schema Validation: Validate JSON Structure schema documents for correctness
  • Instance Validation: Validate JSON data against JSON Structure schemas
  • Schema Export: Generate JSON Structure schemas from Java classes using Jackson type introspection
  • Jackson Converters: Serializers/deserializers for extended numeric and temporal types

Requirements

  • Java 17 or later
  • Maven 3.6 or later

Installation

Add to your pom.xml:

<dependency>
<groupId>org.json-structure</groupId>
<artifactId>json-structure</artifactId>
<version>${version}</version>
</dependency>

Replace ${version} with the desired version number.

Usage

Schema Validation

Validate that a JSON Structure schema is well-formed:

importorg.json_structure.validation.SchemaValidator;
importorg.json_structure.validation.ValidationResult;
SchemaValidatorvalidator = newSchemaValidator();
Stringschema = """ { "$schema": "https://json-structure.org/meta/core/v1.0", "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "int32" } }, "required": ["name"] } """;
ValidationResultresult = validator.validate(schema);
if (result.isValid()) {
System.out.println("Schema is valid!");
} else {
result.getErrors().forEach(e -> System.out.println(e.getMessage()));
}

Instance Validation

Validate JSON data against a schema:

importorg.json_structure.validation.InstanceValidator;
importorg.json_structure.validation.ValidationResult;
InstanceValidatorvalidator = newInstanceValidator();
Stringschema = """ { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "int32", "minimum": 0 } }, "required": ["name"] } """;
Stringinstance = """ { "name": "Alice", "age": 30 } """;
ValidationResultresult = validator.validate(instance, schema);
System.out.println("Valid: " + result.isValid());

Schema Export from Java Types

Generate JSON Structure schemas from Java classes:

importorg.json_structure.schema.JsonStructureSchemaExporter;
importcom.fasterxml.jackson.databind.JsonNode;
importcom.fasterxml.jackson.databind.ObjectMapper;
publicclassPerson {
privateStringname;
privateintage;
privateLocalDatebirthDate;
// getters and setters
}
ObjectMappermapper = newObjectMapper();
JsonNodeschema = JsonStructureSchemaExporter.getSchemaAsNode(Person.class, mapper);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema));

Output:

{
"$schema": "https://json-structure.org/meta/core/v1.0",
"type": "object",
"title": "Person",
"properties": {
"name": { "type": "string" },
"age": { "type": "int32" },
"birthDate": { "type": "date" }
},
"required": ["name", "age", "birthDate"]
}

Jackson Module for Extended Types

Register the JSON Structure module for extended type handling:

importorg.json_structure.converters.JsonStructureModule;
importcom.fasterxml.jackson.databind.ObjectMapper;
ObjectMappermapper = newObjectMapper();
mapper.registerModule(newJsonStructureModule());
// Supports extended numeric types like Int128, UInt128, Decimal, etc.

JVM Language Interoperability

The Java SDK can be used directly from other JVM languages without any wrapper code. The following examples demonstrate how to use the SDK from popular JVM languages.

Kotlin

Kotlin has 100% Java interoperability and is widely used for Android and backend development.

Dependency (Gradle Kotlin DSL)

dependencies {
implementation("org.json-structure:json-structure:${version}")
}

Schema Validation Example

importorg.json_structure.validation.SchemaValidatorimportorg.json_structure.validation.ValidationResultfunmain() {
val validator =SchemaValidator()
val schema =""" { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "int32" } }, "required": ["name"] }"""val result:ValidationResult= validator.validate(schema)
if (result.isValid()) {
println("Schema is valid!")
} else {
result.getErrors().forEach { error ->println(error.getMessage())
}
}
}

Instance Validation Example

importorg.json_structure.validation.InstanceValidatorimportorg.json_structure.validation.ValidationResultfunvalidateInstance() {
val validator =InstanceValidator()
val schema ="""{"type": "string"}"""val instance =""""Hello, World!""""val result:ValidationResult= validator.validate(instance, schema)
println("Valid: ${result.isValid()}")
}

Idiomatic Notes:

  • Use Kotlin's null safety features - the SDK returns non-null results
  • Use val for immutable references (preferred in Kotlin)
  • Leverage Kotlin's string interpolation for output
  • Consider using apply or let for more functional style

Scala

Scala provides full JVM interoperability and is popular in data engineering and functional programming.

Dependency (sbt)

libraryDependencies +="org.json-structure"%"json-structure"%"${version}"

Schema Validation Example

importorg.json_structure.validation.{SchemaValidator, ValidationResult}
importscala.jdk.CollectionConverters._objectSchemaValidationextendsApp {
valvalidator=newSchemaValidator()
valschema="""{ "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "int32" } }, "required": ["name"] }"""valresult:ValidationResult= validator.validate(schema)
if (result.isValid) {
println("Schema is valid!")
} else {
result.getErrors.asScala.foreach(error => println(error.getMessage))
}
}

Instance Validation Example

importorg.json_structure.validation.{InstanceValidator, ValidationResult}
objectInstanceValidationextendsApp {
valvalidator=newInstanceValidator()
valschema="""{"type": "string"}"""valinstance=""""Hello, Scala!""""valresult:ValidationResult= validator.validate(instance, schema)
println(s"Valid: ${result.isValid}")
}

Idiomatic Notes:

  • Use scala.jdk.CollectionConverters._ to convert Java collections to Scala collections
  • Consider wrapping results in Option for functional error handling
  • Use Scala's pattern matching for result processing
  • Leverage Scala's immutable collections when working with validation errors

Groovy

Groovy is a dynamic JVM language used extensively in Gradle and scripting.

Dependency (Gradle Groovy DSL)

dependencies {
implementation 'org.json-structure:json-structure:${version}'
}

Schema Validation Example

importorg.json_structure.validation.SchemaValidatorimportorg.json_structure.validation.ValidationResultdef validator =newSchemaValidator()
def schema ='''{ "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "int32" } }, "required": ["name"]}'''ValidationResult result = validator.validate(schema)
if (result.isValid()) {
println"Schema is valid!"
} else {
result.getErrors().each { error->println error.getMessage()
}
}

Instance Validation Example

importorg.json_structure.validation.InstanceValidatorimportorg.json_structure.validation.ValidationResultdef validator =newInstanceValidator()
def schema ='{"type": "string"}'def instance ='"Hello, Groovy!"'ValidationResult result = validator.validate(instance, schema)
println"Valid: ${result.isValid()}"

Idiomatic Notes:

  • Groovy allows omitting parentheses in many cases for cleaner syntax
  • Use GStrings (double quotes) for string interpolation
  • Collections can be accessed with simplified syntax (.each instead of .forEach)
  • Type declarations are optional but can improve clarity

Clojure

Clojure is a functional Lisp dialect on the JVM with direct Java interoperability.

Dependency (Leiningen)

:dependencies [[org.json-structure/json-structure "${version}"]]

Schema Validation Example

(nsmyapp.validation
(:import [org.json_structure.validation SchemaValidator ValidationResult]))
(defnvalidate-schema []
(let [validator (SchemaValidator.)
schema "{\"type\": \"object\",\"properties\": {\"name\": {\"type\": \"string\"},\"age\": {\"type\": \"int32\"} },\"required\": [\"name\"]}"]
(let [result (.validate validator schema)]
(if (.isValid result)
(println"Schema is valid!")
(doseq [error (.getErrors result)]
(println (.getMessage error)))))))

Instance Validation Example

(nsmyapp.validation
(:import [org.json_structure.validation InstanceValidator ValidationResult]))
(defnvalidate-instance []
(let [validator (InstanceValidator.)
schema "{\"type\": \"string\"}"
instance "\"Hello, Clojure!\""]
(let [result (.validate validator instance schema)]
(println (str"Valid: " (.isValid result))))))

Idiomatic Notes:

  • Use (ClassName.) syntax to create Java objects
  • Call Java methods with (.methodName object args)
  • Java collections can be converted to Clojure sequences with seq
  • Consider using -> or ->> threading macros for cleaner data flow
  • Leverage Clojure's immutable data structures when processing results

Supported Types

Primitive Types

  • boolean - Java boolean/Boolean
  • string - Java String
  • int8 - Java byte/Byte
  • int16 - Java short/Short
  • int32 - Java int/Integer
  • int64 - Java long/Long
  • int128 - Java BigInteger (constrained)
  • uint8 - Java short (0-255)
  • uint16 - Java int (0-65535)
  • uint32 - Java long (0-4294967295)
  • uint64 - Java BigInteger (0-18446744073709551615)
  • uint128 - Java BigInteger (constrained)
  • float - Java float/Float (single-precision 32-bit)
  • double - Java double/Double (double-precision 64-bit)
  • decimal - Java BigDecimal

Temporal Types

  • date - Java LocalDate
  • time - Java LocalTime
  • datetime - Java OffsetDateTime or Instant
  • duration - Java Duration

Other Types

  • uuid - Java UUID
  • uri - Java URI
  • binary - Java byte[] (base64 encoded)

Compound Types

  • object - Java classes/records
  • array - Java List<T>
  • set - Java Set<T>
  • map - Java Map<String, T>
  • tuple - Ordered heterogeneous arrays
  • choice - Discriminated unions

Validation Options

importorg.json_structure.validation.ValidationOptions;
ValidationOptionsoptions = newValidationOptions()
.setStopOnFirstError(false) // Continue collecting all errors
.setMaxValidationDepth(100) // Maximum schema nesting depth
.setAllowDollar(true) // Allow $ in property names (for metaschemas)
.setAllowImport(true) // Enable $import/$importdefs processing
.setExternalSchemas(Map.of( // Sideloaded schemas for import resolution"https://example.com/address.json", addressSchema
));
SchemaValidatorvalidator = newSchemaValidator(options);

Sideloading External Schemas

When using $import to reference external schemas, you can provide those schemas directly instead of fetching them from URIs:

importorg.json_structure.validation.SchemaValidator;
importorg.json_structure.validation.ValidationOptions;
importorg.json_structure.validation.ValidationResult;
importcom.fasterxml.jackson.databind.JsonNode;
importcom.fasterxml.jackson.databind.ObjectMapper;
importjava.util.Map;
ObjectMappermapper = newObjectMapper();
// External schema that would normally be fetchedJsonNodeaddressSchema = mapper.readTree(""" { "$schema": "https://json-structure.org/meta/core/v0/#", "$id": "https://example.com/address.json", "type": "object", "properties": { "street": { "type": "string" }, "city": { "type": "string" } } } """);
// Main schema that imports the address schemaJsonNodemainSchema = mapper.readTree(""" { "$schema": "https://json-structure.org/meta/core/v0/#", "type": "object", "properties": { "name": { "type": "string" }, "address": { "$ref": "#/definitions/Imported/Address" } }, "definitions": { "Imported": { "$import": "https://example.com/address.json" } } } """);
// Sideload the address schema - keyed by URIValidationOptionsoptions = newValidationOptions()
.setAllowImport(true)
.setExternalSchemas(Map.of(
"https://example.com/address.json", addressSchema
));
SchemaValidatorvalidator = newSchemaValidator(options);
ValidationResultresult = validator.validate(mainSchema);
System.out.println("Valid: " + result.isValid());

Building

mvn clean package

Testing

mvn test

License

MIT License - see LICENSE file for details.