Skip to content

Commit 0801b98

Browse files
authored
Create JsonSchemaLoader interface to support external schema loading (#42)
Resolves #27
1 parent 45aaa7a commit 0801b98

File tree

24 files changed

+1466
-142
lines changed

24 files changed

+1466
-142
lines changed

.ci-python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10

.github/workflows/check.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ jobs:
2424
with:
2525
distribution: temurin
2626
java-version-file: .ci-java-version
27+
- uses: actions/setup-python@v5
28+
with:
29+
python-version-file: .ci-python-version
2730
- name: Validate Gradle Wrapper
2831
uses: gradle/[email protected]
2932
- name: Cache konan

.github/workflows/pull_request.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ name: "Check the PR"
22

33
on:
44
pull_request:
5+
paths-ignore:
6+
- 'README.md'
7+
- 'changelog_config.json'
58

69
concurrency:
710
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ jobs:
3737
with:
3838
distribution: temurin
3939
java-version-file: .ci-java-version
40+
- uses: actions/setup-python@v5
41+
with:
42+
python-version-file: .ci-python-version
4043
- name: Validate Gradle Wrapper
4144
uses: gradle/[email protected]
4245
- name: Cache konan

.github/workflows/snapshot_release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717
with:
1818
distribution: temurin
1919
java-version-file: .ci-java-version
20+
- uses: actions/setup-python@v5
21+
with:
22+
python-version-file: .ci-python-version
2023
- name: Validate Gradle Wrapper
2124
uses: gradle/[email protected]
2225
- name: Cache konan

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ val valid = schema.validate(elementToValidate, errors::add)
133133
|:------------|:----------------------------------------------------------------------------------------------------|
134134
| $id | Supported. $id in sub-schemas are collected as well and can be used in $ref |
135135
| $schema | Supported. Validates if schema is one of the supported schemas. The last supported is used if empty |
136-
| $ref | Supported (except references to schemas from another document) |
136+
| $ref | Supported |
137137
| definitions | Supported. Definitions are loaded and can be referenced |
138138

139139
- Assertions
@@ -180,8 +180,8 @@ val valid = schema.validate(elementToValidate, errors::add)
180180
|:------------------|:----------------------------------------------------------------------------------------------------|
181181
| $id | Supported. $id in sub-schemas are collected as well and can be used in $ref |
182182
| $schema | Supported. Validates if schema is one of the supported schemas. The last supported is used if empty |
183-
| $ref | Supported (except references to schemas from another document) |
184-
| $recursiveRef | Supported (does not work yet to extend schemas from other documents) |
183+
| $ref | Supported |
184+
| $recursiveRef | Supported |
185185
| $defs/definitions | Supported. Definitions are loaded and can be referenced |
186186

187187
- Assertions
@@ -233,8 +233,8 @@ val valid = schema.validate(elementToValidate, errors::add)
233233
|:---------------------------|:----------------------------------------------------------------------------------------------------|
234234
| $id | Supported. $id in sub-schemas are collected as well and can be used in $ref |
235235
| $schema | Supported. Validates if schema is one of the supported schemas. The last supported is used if empty |
236-
| $ref | Supported (except references to schemas from another document) |
237-
| $dynamicRef/$dynamicAnchor | Supported (does not work yet to extend schemas from other documents) |
236+
| $ref | Supported |
237+
| $dynamicRef/$dynamicAnchor | Supported |
238238
| $defs/definitions | Supported. Definitions are loaded and can be referenced |
239239

240240
- Assertions
@@ -284,6 +284,9 @@ as a part of the CI to make sure the validation meet the expected behavior.
284284
Not everything is supported right now but the missing functionality might be added in the future.
285285
The test are located [here](test-suites).
286286

287+
**NOTE:** _Python 3.* is required to run test-suites._
288+
_It is used to generate list of remote schemas using [this script](test-suites/schema-test-suite/bin/jsonschema_suite)_
289+
287290
## Developer notes
288291

289292
The update to Kotlin 1.9.22 came with an issue for JS incremental compilation.
@@ -296,7 +299,7 @@ In case you see an error about main function that already bind please execute `c
296299
- [x] Add support for newer drafts
297300
- [x] [Draft 2019-09 (Draft 8)](https://json-schema.org/specification-links.html#draft-2019-09-formerly-known-as-draft-8)
298301
- [x] [2020-12](https://json-schema.org/specification-links.html#2020-12)
299-
- [ ] Add support for schemas from external documents
300-
- [ ] Load schemas from local sources
302+
- [x] Add support for schemas from external documents
303+
- [x] Load schemas from local sources
301304
- [ ] Load schemas from remote sources
302305
- [ ] Formalize error output as it is defined in the latest drafts (have not fully decided if it should be done)

api/json-schema-validator.api

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,35 @@ public final class io/github/optimumcode/json/schema/JsonSchema$Companion {
5656
public static synthetic fun fromJsonElement$default (Lio/github/optimumcode/json/schema/JsonSchema$Companion;Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/SchemaType;ILjava/lang/Object;)Lio/github/optimumcode/json/schema/JsonSchema;
5757
}
5858

59+
public abstract interface class io/github/optimumcode/json/schema/JsonSchemaLoader {
60+
public static final field Companion Lio/github/optimumcode/json/schema/JsonSchemaLoader$Companion;
61+
public static fun create ()Lio/github/optimumcode/json/schema/JsonSchemaLoader;
62+
public abstract fun fromDefinition (Ljava/lang/String;)Lio/github/optimumcode/json/schema/JsonSchema;
63+
public abstract fun fromDefinition (Ljava/lang/String;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchema;
64+
public abstract fun fromJsonElement (Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/JsonSchema;
65+
public abstract fun fromJsonElement (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchema;
66+
public abstract fun register (Ljava/lang/String;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
67+
public abstract fun register (Ljava/lang/String;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
68+
public abstract fun register (Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
69+
public abstract fun register (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
70+
public abstract fun register (Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
71+
public abstract fun register (Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
72+
public abstract fun registerWellKnown (Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
73+
}
74+
75+
public final class io/github/optimumcode/json/schema/JsonSchemaLoader$Companion {
76+
public final fun create ()Lio/github/optimumcode/json/schema/JsonSchemaLoader;
77+
}
78+
79+
public final class io/github/optimumcode/json/schema/JsonSchemaLoader$DefaultImpls {
80+
public static fun fromDefinition (Lio/github/optimumcode/json/schema/JsonSchemaLoader;Ljava/lang/String;)Lio/github/optimumcode/json/schema/JsonSchema;
81+
public static fun fromJsonElement (Lio/github/optimumcode/json/schema/JsonSchemaLoader;Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/JsonSchema;
82+
public static fun register (Lio/github/optimumcode/json/schema/JsonSchemaLoader;Ljava/lang/String;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
83+
public static fun register (Lio/github/optimumcode/json/schema/JsonSchemaLoader;Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
84+
public static fun register (Lio/github/optimumcode/json/schema/JsonSchemaLoader;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
85+
public static fun registerWellKnown (Lio/github/optimumcode/json/schema/JsonSchemaLoader;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchemaLoader;
86+
}
87+
5988
public final class io/github/optimumcode/json/schema/JsonSchemaStream {
6089
public static final fun fromStream (Lio/github/optimumcode/json/schema/JsonSchema$Companion;Ljava/io/InputStream;)Lio/github/optimumcode/json/schema/JsonSchema;
6190
}

src/commonMain/kotlin/io/github/optimumcode/json/schema/JsonSchema.kt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package io.github.optimumcode.json.schema
22

33
import io.github.optimumcode.json.pointer.JsonPointer
4-
import io.github.optimumcode.json.schema.internal.AssertionWithPath
54
import io.github.optimumcode.json.schema.internal.DefaultAssertionContext
65
import io.github.optimumcode.json.schema.internal.DefaultReferenceResolver
6+
import io.github.optimumcode.json.schema.internal.IsolatedLoader
77
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
8-
import io.github.optimumcode.json.schema.internal.RefId
9-
import io.github.optimumcode.json.schema.internal.SchemaLoader
10-
import kotlinx.serialization.json.Json
118
import kotlinx.serialization.json.JsonElement
129
import kotlin.jvm.JvmOverloads
1310
import kotlin.jvm.JvmStatic
@@ -18,7 +15,7 @@ import kotlin.jvm.JvmStatic
1815
*/
1916
public class JsonSchema internal constructor(
2017
private val assertion: JsonSchemaAssertion,
21-
private val references: Map<RefId, AssertionWithPath>,
18+
private val referenceResolver: DefaultReferenceResolver,
2219
) {
2320
/**
2421
* Validates [value] against this [JsonSchema].
@@ -31,7 +28,7 @@ public class JsonSchema internal constructor(
3128
value: JsonElement,
3229
errorCollector: ErrorCollector,
3330
): Boolean {
34-
val context = DefaultAssertionContext(JsonPointer.ROOT, DefaultReferenceResolver(references))
31+
val context = DefaultAssertionContext(JsonPointer.ROOT, referenceResolver)
3532
return assertion.validate(value, context, errorCollector)
3633
}
3734

@@ -47,10 +44,7 @@ public class JsonSchema internal constructor(
4744
public fun fromDefinition(
4845
schema: String,
4946
defaultType: SchemaType? = null,
50-
): JsonSchema {
51-
val schemaElement: JsonElement = Json.parseToJsonElement(schema)
52-
return fromJsonElement(schemaElement, defaultType)
53-
}
47+
): JsonSchema = IsolatedLoader.fromDefinition(schema, defaultType)
5448

5549
/**
5650
* Loads JSON schema from the [schemaElement] JSON element
@@ -63,8 +57,6 @@ public class JsonSchema internal constructor(
6357
public fun fromJsonElement(
6458
schemaElement: JsonElement,
6559
defaultType: SchemaType? = null,
66-
): JsonSchema {
67-
return SchemaLoader().load(schemaElement, defaultType)
68-
}
60+
): JsonSchema = IsolatedLoader.fromJsonElement(schemaElement, defaultType)
6961
}
7062
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.github.optimumcode.json.schema
2+
3+
import io.github.optimumcode.json.schema.SchemaType.DRAFT_2019_09
4+
import io.github.optimumcode.json.schema.SchemaType.DRAFT_2020_12
5+
import io.github.optimumcode.json.schema.SchemaType.DRAFT_7
6+
import io.github.optimumcode.json.schema.internal.SchemaLoader
7+
import io.github.optimumcode.json.schema.internal.wellknown.Draft201909
8+
import io.github.optimumcode.json.schema.internal.wellknown.Draft202012
9+
import io.github.optimumcode.json.schema.internal.wellknown.Draft7
10+
import kotlinx.serialization.json.JsonElement
11+
import kotlin.jvm.JvmStatic
12+
13+
public interface JsonSchemaLoader {
14+
public fun registerWellKnown(draft: SchemaType): JsonSchemaLoader =
15+
apply {
16+
when (draft) {
17+
DRAFT_7 -> Draft7.entries.forEach { register(it.content) }
18+
DRAFT_2019_09 -> Draft201909.entries.forEach { register(it.content) }
19+
DRAFT_2020_12 -> Draft202012.entries.forEach { register(it.content) }
20+
}
21+
}
22+
23+
public fun register(schema: JsonElement): JsonSchemaLoader = register(schema, null)
24+
25+
public fun register(
26+
schema: JsonElement,
27+
draft: SchemaType?,
28+
): JsonSchemaLoader
29+
30+
public fun register(schema: String): JsonSchemaLoader = register(schema, null)
31+
32+
public fun register(
33+
schema: String,
34+
draft: SchemaType?,
35+
): JsonSchemaLoader
36+
37+
public fun register(
38+
schema: JsonElement,
39+
remoteUri: String,
40+
): JsonSchemaLoader = register(schema, remoteUri, null)
41+
42+
public fun register(
43+
schema: JsonElement,
44+
remoteUri: String,
45+
draft: SchemaType?,
46+
): JsonSchemaLoader
47+
48+
public fun fromDefinition(schema: String): JsonSchema = fromDefinition(schema, null)
49+
50+
public fun fromDefinition(
51+
schema: String,
52+
draft: SchemaType?,
53+
): JsonSchema
54+
55+
public fun fromJsonElement(schemaElement: JsonElement): JsonSchema = fromJsonElement(schemaElement, null)
56+
57+
public fun fromJsonElement(
58+
schemaElement: JsonElement,
59+
draft: SchemaType?,
60+
): JsonSchema
61+
62+
public companion object {
63+
@JvmStatic
64+
public fun create(): JsonSchemaLoader = SchemaLoader()
65+
}
66+
}

src/commonMain/kotlin/io/github/optimumcode/json/schema/SchemaType.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public enum class SchemaType(
2828
// so, it definitely is not a supported schema ID
2929
return null
3030
}
31-
return values().find {
31+
return entries.find {
3232
it.schemaId.run {
3333
host == uri.host &&
3434
port == uri.port &&

0 commit comments

Comments
 (0)