You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pe...@apache.org on 2026/08/10 04:38:35 UTC
(celix) 05/35: Add URI validation support for JSON Schema with comprehensive test cases
This is an automated email from the ASF dual-hosted git repository.
PengZheng pushed a commit to branch feature/json-schema
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 6e29773b993b9d14a5a1039fa8bb319bcaec2afc
Author: PengZheng <ho...@gmail.com>
AuthorDate: Wed Aug 5 11:34:38 2026 +0800
Add URI validation support for JSON Schema with comprehensive test cases
---
cmake/celix_project/CodeCoverage.cmake | 4 +-
libs/jansson_ext/gtest/CMakeLists.txt | 1 +
libs/jansson_ext/gtest/src/test_validate_uri.cpp | 506 +++++++++++++++++++++++
libs/jansson_ext/src/celix_jansson_schema.c | 37 +-
4 files changed, 544 insertions(+), 4 deletions(-)
diff --git a/cmake/celix_project/CodeCoverage.cmake b/cmake/celix_project/CodeCoverage.cmake
index 7615b38fc..965bf0053 100644
--- a/cmake/celix_project/CodeCoverage.cmake
+++ b/cmake/celix_project/CodeCoverage.cmake
@@ -130,12 +130,12 @@ function (setup_target_for_coverage)
# Cleanup lcov
COMMAND ${LCOV_PATH} --directory . --zerocounters
- COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R ${TEST_TARGET_NAME}
+ # COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -R ${TEST_TARGET_NAME}
# Capturing lcov counters and generating report
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage
COMMAND ${LCOV_PATH} --directory ${COVERAGE_SCAN_DIR} --capture --output-file ${OUTPUT_FILE}
- COMMAND ${LCOV_PATH} --remove ${OUTPUT_FILE} '**/error_injector/*' '**/mock/*' '**/.conan/*' '**/test/*' '**/gtest/*' '**/tst/*' '**/celix/gen/*' '**/googletest_project/*' '**/glog/*' '/usr/*' --output-file ${OUTPUT_FILE}.cleaned
+ COMMAND ${LCOV_PATH} --ignore-errors unused --remove ${OUTPUT_FILE} '**/error_injector/*' '**/mock/*' '**/.conan/*' '**/test/*' '**/gtest/*' '**/tst/*' '**/celix/gen/*' '**/googletest_project/*' '**/glog/*' '/usr/*' --output-file ${OUTPUT_FILE}.cleaned
#test dependencies, so that test is runned
DEPENDENCIES ${TEST_TARGET_NAME}
diff --git a/libs/jansson_ext/gtest/CMakeLists.txt b/libs/jansson_ext/gtest/CMakeLists.txt
index 82138dda5..a52bbcc9d 100644
--- a/libs/jansson_ext/gtest/CMakeLists.txt
+++ b/libs/jansson_ext/gtest/CMakeLists.txt
@@ -29,6 +29,7 @@ set(TEST_SOURCES
src/test_pointer.cpp
src/test_patch.cpp
src/test_json_patch.cpp
+ src/test_validate_uri.cpp
)
foreach(T ${TEST_SOURCES})
diff --git a/libs/jansson_ext/gtest/src/test_validate_uri.cpp b/libs/jansson_ext/gtest/src/test_validate_uri.cpp
new file mode 100644
index 000000000..b782ff4b0
--- /dev/null
+++ b/libs/jansson_ext/gtest/src/test_validate_uri.cpp
@@ -0,0 +1,506 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_common.h"
+
+/* ── validate_uri("#") — equivalent to validate() ──────────────────────── */
+
+TEST(ValidateUriTest, RootSchemaViaHash) {
+ static const char* schema = R"({
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" }
+ },
+ "required": ["name"]
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Valid: has required "name" property */
+ json_t* inst = json_loads(R"({"name":"test"})", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Invalid: missing required "name" property */
+ inst = json_loads(R"({})", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri(NULL) — equivalent to validate() ─────────────────────── */
+
+TEST(ValidateUriTest, RootSchemaViaNull) {
+ static const char* schema = R"({
+ "type": "string",
+ "minLength": 3
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Valid: string long enough */
+ json_t* inst = json_loads(R"("abc")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, nullptr, capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Invalid: string too short */
+ inst = json_loads(R"("ab")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, nullptr, capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri("#/definitions/MyType") — subschema validation ──────── */
+
+TEST(ValidateUriTest, DefinitionsSubschema) {
+ static const char* schema = R"({
+ "definitions": {
+ "PositiveInt": {
+ "type": "integer",
+ "minimum": 1
+ },
+ "ShortString": {
+ "type": "string",
+ "maxLength": 5
+ }
+ },
+ "type": "object"
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Validate against PositiveInt: 5 is valid */
+ json_t* inst = json_loads("5", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#/definitions/PositiveInt", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Validate against PositiveInt: 0 is invalid (minimum=1) */
+ inst = json_loads("0", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/definitions/PositiveInt", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ /* Validate against ShortString: "hello" is valid (len=5) */
+ inst = json_loads(R"("hello")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#/definitions/ShortString", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Validate against ShortString: "too long" is invalid (>5) */
+ inst = json_loads(R"("too long")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/definitions/ShortString", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ /* Validate against ShortString: integer is rejected (type mismatch) */
+ inst = json_loads("123", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/definitions/ShortString", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri("#/properties/name") — property subschema ───────────── */
+
+TEST(ValidateUriTest, PropertySubschema) {
+ static const char* schema = R"({
+ "type": "object",
+ "properties": {
+ "email": {
+ "type": "string",
+ "format": "email"
+ },
+ "age": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 150
+ }
+ }
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Validate against the "age" property schema: 25 is valid */
+ json_t* inst = json_loads("25", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#/properties/age", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Validate against "age": 200 is invalid (>150) */
+ inst = json_loads("200", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/properties/age", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ /* Validate against "age": string is invalid (type mismatch) */
+ inst = json_loads(R"("twenty-five")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/properties/age", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri with empty string ────────────────────────────────────── */
+
+TEST(ValidateUriTest, EmptyStringUri) {
+ static const char* schema = R"({
+ "type": "integer",
+ "minimum": 10
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Empty string URI should fallback to root schema */
+ json_t* inst = json_loads("15", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ inst = json_loads("5", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri with non-existent fragment ───────────────────────────── */
+
+TEST(ValidateUriTest, NonExistentFragmentFallback) {
+ static const char* schema = R"({
+ "definitions": {
+ "Foo": { "type": "string" }
+ }
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Non-existent fragment "#/definitions/NonExistent" — fallback to root schema
+ * (root schema has no constraints, so anything passes) */
+ json_t* inst = json_loads("42", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#/definitions/NonExistent", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri with $ref-based subschema ────────────────────────────── */
+
+TEST(ValidateUriTest, SubschemaReachedViaRef) {
+ static const char* schema = R"({
+ "definitions": {
+ "EmailStr": {
+ "type": "string",
+ "minLength": 5
+ }
+ },
+ "properties": {
+ "email": { "$ref": "#/definitions/EmailStr" }
+ }
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Validate against EmailStr definition directly */
+ json_t* inst = json_loads(R"("hello@test.com")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#/definitions/EmailStr", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Too short for EmailStr */
+ inst = json_loads(R"("hi")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/definitions/EmailStr", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ /* Type mismatch for EmailStr */
+ inst = json_loads("12345", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/definitions/EmailStr", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri with boolean subschema ───────────────────────────────── */
+
+TEST(ValidateUriTest, BooleanSubschema) {
+ static const char* schema = R"({
+ "definitions": {
+ "AlwaysTrue": true,
+ "AlwaysFalse": false
+ }
+ })";
+
+ auto* v = make_validator();
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* true schema — accepts anything */
+ json_t* inst = json_loads("42", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst, "#/definitions/AlwaysTrue", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* false schema — rejects everything */
+ inst = json_loads(R"("anything")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst, "#/definitions/AlwaysFalse", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri with external URI (no fragment) ─────────────────────── */
+
+static int ext_schema_loader(const char* /*uri*/, json_t** out, void* /*ud*/) {
+ /* Return a schema for any requested URI */
+ *out = json_loads(R"({
+ "$id": "http://example.com/number.json",
+ "type": "number",
+ "minimum": 10
+ })", 0, nullptr);
+ return *out ? CELIX_JANSSON_SCHEMA_OK : CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
+}
+
+TEST(ValidateUriTest, ExternalUriNoFragment) {
+ /* Root schema references an external schema, which gets loaded and
+ * registered. Then we validate directly against that external URI
+ * with no fragment — exercising the empty-fragment lookup path. */
+ static const char* schema = R"({
+ "type": "object",
+ "properties": {
+ "score": { "$ref": "http://example.com/number.json" }
+ }
+ })";
+
+ auto* v = celix_jansson_schema_validator_create(
+ ext_schema_loader, nullptr,
+ celix_jansson_schema_default_format_check, nullptr,
+ nullptr, nullptr);
+ ASSERT_NE(nullptr, v);
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Validate against the external schema's root (no fragment) */
+ json_t* inst = json_loads("15", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst,
+ "http://example.com/number.json", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Invalid: below minimum=10 */
+ inst = json_loads("5", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst,
+ "http://example.com/number.json", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ /* Invalid: wrong type */
+ inst = json_loads(R"("hello")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst,
+ "http://example.com/number.json", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ free_validator(v);
+}
+
+/* ── validate_uri with external URI + fragment ─────────────────────────── */
+
+TEST(ValidateUriTest, ExternalUriWithFragment) {
+ /* Same external schema with a definitions entry */
+ static const char* ext_schema_json = R"({
+ "$id": "http://example.com/types.json",
+ "definitions": {
+ "Color": {
+ "type": "string",
+ "enum": ["red", "green", "blue"]
+ }
+ }
+ })";
+
+ /* Cache the schema for the loader */
+ json_t* ext_schema = json_loads(ext_schema_json, 0, nullptr);
+ ASSERT_NE(nullptr, ext_schema);
+
+ auto loader = [](const char* /*uri*/, json_t** out, void* ud) -> int {
+ json_t* cached = (json_t*)ud;
+ *out = json_deep_copy(cached);
+ return *out ? CELIX_JANSSON_SCHEMA_OK : CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
+ };
+
+ auto* v = celix_jansson_schema_validator_create(
+ loader, ext_schema,
+ celix_jansson_schema_default_format_check, nullptr,
+ nullptr, nullptr);
+ ASSERT_NE(nullptr, v);
+
+ static const char* schema = R"({
+ "type": "object",
+ "properties": {
+ "color": { "$ref": "http://example.com/types.json#/definitions/Color" }
+ }
+ })";
+
+ json_t* sch = json_loads(schema, 0, nullptr);
+ ASSERT_NE(nullptr, sch);
+
+ char* errmsg = nullptr;
+ int rc = celix_jansson_schema_set_root_schema(v, sch, &errmsg);
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, rc) << (errmsg ? errmsg : "");
+ free(errmsg);
+
+ /* Validate against the external schema's Color definition */
+ json_t* inst = json_loads(R"("red")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_EQ(0, celix_jansson_schema_validate_uri(v, inst,
+ "http://example.com/types.json#/definitions/Color", capture_error, nullptr, nullptr));
+ json_decref(inst);
+
+ /* Invalid enum value */
+ inst = json_loads(R"("yellow")", JSON_DECODE_ANY, nullptr);
+ ASSERT_NE(nullptr, inst);
+ reset_errors();
+ EXPECT_GT(celix_jansson_schema_validate_uri(v, inst,
+ "http://example.com/types.json#/definitions/Color", capture_error, nullptr, nullptr), 0);
+ json_decref(inst);
+
+ json_decref(sch);
+ json_decref(ext_schema);
+ free_validator(v);
+}
diff --git a/libs/jansson_ext/src/celix_jansson_schema.c b/libs/jansson_ext/src/celix_jansson_schema.c
index 3a7027352..8cd620520 100644
--- a/libs/jansson_ext/src/celix_jansson_schema.c
+++ b/libs/jansson_ext/src/celix_jansson_schema.c
@@ -2339,12 +2339,45 @@ int celix_jansson_schema_root_validate(celix_jansson_schema_root_t* root,
celix_jansson_validation_context_t* ctx) {
if (!root || !ctx)
return -1;
- celix_jansson_schema_node_t* sch = root->root;
+
+ celix_jansson_schema_node_t* sch = NULL;
+
+ /* Resolve initial_uri to a specific subschema, if provided */
+ if (initial_uri && initial_uri[0] != '\0' && strcmp(initial_uri, "#") != 0) {
+ celix_jansson_uri_t uri;
+ if (celix_jansson_uri_init(&uri, initial_uri) == 0) {
+ char* loc = celix_jansson_uri_location(&uri);
+ const char* frag = celix_jansson_uri_fragment(&uri);
+
+ celix_jansson_schema_file_t* sf =
+ (celix_jansson_schema_file_t*)celix_jansson_hash_table_get(&root->files, loc);
+ if (sf) {
+ if (frag && frag[0] != '\0') {
+ sch = (celix_jansson_schema_node_t*)celix_jansson_hash_table_get(&sf->schemas, frag);
+ if (!sch) {
+ /* Attempt on-demand compilation from the original document */
+ sch = resolve_document_fragment(root, loc, frag);
+ }
+ } else {
+ /* Empty fragment — resolve the root of this file */
+ sch = (celix_jansson_schema_node_t*)celix_jansson_hash_table_get(&sf->schemas, "");
+ }
+ }
+ free(loc);
+ free((char*)frag);
+ celix_jansson_uri_clear(&uri);
+ }
+ }
+
+ /* Fallback: use root schema */
+ if (!sch)
+ sch = root->root;
+
if (!sch) {
ctx->sink->emit(ctx->sink, "", instance, "no root schema set");
return 1;
}
- (void)initial_uri;
+
celix_jansson_path_t path;
celix_jansson_path_init(&path);
int errs = sch->vtable->validate(sch, instance, &path, ctx);