Skip to content

Commit

Permalink
Implement a function to turn absolute schema references into relative (
Browse files Browse the repository at this point in the history
…#1424)

Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti authored Jan 7, 2025
1 parent 30bdbfd commit 08ea8d9
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/jsonschema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ noa_library(NAMESPACE sourcemeta PROJECT jsontoolkit NAME jsonschema
PRIVATE_HEADERS anchor.h bundle.h resolver.h
walker.h reference.h frame.h error.h unevaluated.h keywords.h
SOURCES jsonschema.cc default_walker.cc frame.cc
anchor.cc resolver.cc walker.cc bundle.cc unevaluated.cc
anchor.cc resolver.cc walker.cc bundle.cc
unevaluated.cc relativize.cc
"${CMAKE_CURRENT_BINARY_DIR}/official_resolver.cc")

if(JSONTOOLKIT_INSTALL)
Expand Down
36 changes: 36 additions & 0 deletions src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,42 @@ SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT
auto schema_format_compare(const JSON::String &left, const JSON::String &right)
-> bool;

/// @ingroup jsonschema
///
/// Try to turn every possible absolute reference in a schema into a relative
/// one. For example:
///
/// ```cpp
/// #include <sourcemeta/jsontoolkit/json.h>
/// #include <sourcemeta/jsontoolkit/jsonschema.h>
/// #include <cassert>
///
/// sourcemeta::jsontoolkit::JSON document =
/// sourcemeta::jsontoolkit::parse(R"JSON({
/// "$id": "https://www.example.com/schema",
/// "$schema": "https://json-schema.org/draft/2020-12/schema",
/// "$ref": "https://www.example.com/another",
/// })JSON");
///
/// sourcemeta::jsontoolkit::relativize(schema,
/// sourcemeta::jsontoolkit::default_dialect,
/// sourcemeta::jsontoolkit::official_resolver);
///
/// const sourcemeta::jsontoolkit::JSON expected =
/// sourcemeta::jsontoolkit::parse(R"JSON({
/// "$id": "https://www.example.com/schema",
/// "$schema": "https://json-schema.org/draft/2020-12/schema",
/// "$ref": "another",
/// })JSON");
///
/// assert(document == expected);
/// ```
SOURCEMETA_JSONTOOLKIT_JSONSCHEMA_EXPORT
auto relativize(
JSON &schema, const SchemaWalker &walker, const SchemaResolver &resolver,
const std::optional<std::string> &default_dialect = std::nullopt,
const std::optional<std::string> &default_id = std::nullopt) -> void;

} // namespace sourcemeta::jsontoolkit

#endif
43 changes: 43 additions & 0 deletions src/jsonschema/relativize.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <sourcemeta/jsontoolkit/jsonschema.h>

namespace sourcemeta::jsontoolkit {

auto relativize(JSON &schema, const SchemaWalker &walker,
const SchemaResolver &resolver,
const std::optional<std::string> &default_dialect,
const std::optional<std::string> &default_id) -> void {
Frame frame;
frame.analyse(schema, walker, resolver, default_dialect, default_id);

for (const auto &entry : frame.locations()) {
if (entry.second.type != Frame::LocationType::Resource &&
entry.second.type != Frame::LocationType::Subschema) {
continue;
}

auto &subschema{get(schema, entry.second.pointer)};
assert(is_schema(subschema));
if (!subschema.is_object()) {
continue;
}

const auto base{URI{entry.second.base}.canonicalize()};
for (const auto &property : subschema.as_object()) {
if (walker(property.first, frame.vocabularies(entry.second, resolver))
.type != KeywordType::Reference ||
!property.second.is_string()) {
continue;
}

URI reference{property.second.to_string()};
reference.canonicalize();
reference.relative_to(base);

if (reference.is_relative()) {
subschema.assign(property.first, JSON{reference.recompose()});
}
}
}
}

} // namespace sourcemeta::jsontoolkit
1 change: 1 addition & 0 deletions test/jsonschema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ add_executable(sourcemeta_jsontoolkit_jsonschema_unit
jsonschema_error_test.cc
jsonschema_keyword_iterator_test.cc
jsonschema_official_resolver_test.cc
jsonschema_relativize_test.cc
jsonschema_map_resolver_test.cc
jsonschema_flat_file_resolver_test.cc
jsonschema_format_test.cc)
Expand Down
Loading

4 comments on commit 08ea8d9

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (macos/llvm)

Benchmark suite Current: 08ea8d9 Previous: 30bdbfd Ratio
JSON_Array_Of_Objects_Unique 363.1005432169859 ns/iter 328.5681976816777 ns/iter 1.11
JSON_Parse_1 29350.68247765509 ns/iter 21290.004764232217 ns/iter 1.38
JSON_Fast_Hash_Helm_Chart_Lock 55.44076688666669 ns/iter 47.63555569682142 ns/iter 1.16
JSON_Equality_Helm_Chart_Lock 159.97529624680226 ns/iter 140.6939839185073 ns/iter 1.14
Regex_Lower_S_Or_Upper_S_Asterisk 1.8258450555993455 ns/iter 1.7210000096164468 ns/iter 1.06
Regex_Caret_Lower_S_Or_Upper_S_Asterisk_Dollar 1.8632866279841254 ns/iter 1.6917191849503839 ns/iter 1.10
Regex_Period_Asterisk 1.9398793997879733 ns/iter 1.6213994593405456 ns/iter 1.20
Regex_Group_Period_Asterisk_Group 1.694898179660882 ns/iter 1.658456141472408 ns/iter 1.02
Regex_Period_Plus 2.1054609646761224 ns/iter 2.066603532281212 ns/iter 1.02
Regex_Period 2.159344542769177 ns/iter 1.9070880685432858 ns/iter 1.13
Regex_Caret_Period_Plus_Dollar 2.0020823116657884 ns/iter 1.9765349226844413 ns/iter 1.01
Regex_Caret_Group_Period_Plus_Group_Dollar 2.225401451980401 ns/iter 2.04901840075823 ns/iter 1.09
Regex_Caret_Period_Asterisk_Dollar 1.7861854657756169 ns/iter 1.6666231745930804 ns/iter 1.07
Regex_Caret_Group_Period_Asterisk_Group_Dollar 1.6737101808159158 ns/iter 1.718850463944502 ns/iter 0.97
Regex_Caret_X_Hyphen 6.630473399527316 ns/iter 7.154652071213014 ns/iter 0.93
Regex_Period_Md_Dollar 72.12473806286219 ns/iter 76.25686040415383 ns/iter 0.95
Regex_Caret_Slash_Period_Asterisk 6.215811336759792 ns/iter 6.692913296943126 ns/iter 0.93
Regex_Caret_Period_Range_Dollar 2.442503672773014 ns/iter 2.2936350044681584 ns/iter 1.06
Regex_Nested_Backtrack 778.1836685264979 ns/iter 853.4459445368984 ns/iter 0.91
Pointer_Object_Traverse 15.889012852370337 ns/iter 15.561300927368134 ns/iter 1.02
Pointer_Object_Try_Traverse 33.4104883926905 ns/iter 33.83626335739549 ns/iter 0.99
Pointer_Push_Back_Pointer_To_Weak_Pointer 183.63642202813023 ns/iter 196.3917889036921 ns/iter 0.94

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (linux/llvm)

Benchmark suite Current: 08ea8d9 Previous: 30bdbfd Ratio
JSON_Array_Of_Objects_Unique 448.99102806627013 ns/iter 444.7029717190713 ns/iter 1.01
JSON_Parse_1 29977.227723618562 ns/iter 30094.860889346444 ns/iter 1.00
JSON_Fast_Hash_Helm_Chart_Lock 54.68012567188576 ns/iter 54.747892524737814 ns/iter 1.00
JSON_Equality_Helm_Chart_Lock 149.48033985843307 ns/iter 153.6262297509604 ns/iter 0.97
Regex_Lower_S_Or_Upper_S_Asterisk 2.203977063137023 ns/iter 2.4889765445066074 ns/iter 0.89
Regex_Caret_Lower_S_Or_Upper_S_Asterisk_Dollar 2.2104484593051104 ns/iter 2.2030498602022406 ns/iter 1.00
Regex_Period_Asterisk 2.1991541543015054 ns/iter 2.395607758053087 ns/iter 0.92
Regex_Group_Period_Asterisk_Group 2.1921508727627614 ns/iter 2.205967351655029 ns/iter 0.99
Regex_Period_Plus 2.486290124674428 ns/iter 2.7992309449809905 ns/iter 0.89
Regex_Period 2.4854984013104757 ns/iter 2.4940700463051075 ns/iter 1.00
Regex_Caret_Period_Plus_Dollar 2.4859983770520535 ns/iter 2.489684124547867 ns/iter 1.00
Regex_Caret_Group_Period_Plus_Group_Dollar 2.485744414467124 ns/iter 2.4872173738739063 ns/iter 1.00
Regex_Caret_Period_Asterisk_Dollar 3.416262530572305 ns/iter 3.4195522694475806 ns/iter 1.00
Regex_Caret_Group_Period_Asterisk_Group_Dollar 2.1950793354480327 ns/iter 3.4211430125051834 ns/iter 0.64
Regex_Caret_X_Hyphen 13.048821086520212 ns/iter 12.554150853321017 ns/iter 1.04
Regex_Period_Md_Dollar 74.47197122949497 ns/iter 75.48205857211585 ns/iter 0.99
Regex_Caret_Slash_Period_Asterisk 6.22303867854373 ns/iter 7.146219596969072 ns/iter 0.87
Regex_Caret_Period_Range_Dollar 3.735530556719767 ns/iter 3.728600153351832 ns/iter 1.00
Regex_Nested_Backtrack 494.4804998267498 ns/iter 478.1982704156642 ns/iter 1.03
Pointer_Object_Traverse 44.78886507599523 ns/iter 45.35063209246556 ns/iter 0.99
Pointer_Object_Try_Traverse 52.65845835403999 ns/iter 52.2768754337474 ns/iter 1.01
Pointer_Push_Back_Pointer_To_Weak_Pointer 352.25705860426945 ns/iter 353.2517207047232 ns/iter 1.00

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (windows/msvc)

Benchmark suite Current: 08ea8d9 Previous: 30bdbfd Ratio
JSON_Array_Of_Objects_Unique 418.99474403936665 ns/iter 412.4324101592953 ns/iter 1.02
JSON_Parse_1 79744.63640015424 ns/iter 82066.19419642688 ns/iter 0.97
JSON_Fast_Hash_Helm_Chart_Lock 54.36856000000034 ns/iter 53.192625000002224 ns/iter 1.02
JSON_Equality_Helm_Chart_Lock 217.9135058967219 ns/iter 216.53843676169143 ns/iter 1.01
Regex_Lower_S_Or_Upper_S_Asterisk 8.165690588545985 ns/iter 8.224203125000479 ns/iter 0.99
Regex_Caret_Lower_S_Or_Upper_S_Asterisk_Dollar 7.798883000898016 ns/iter 7.925596839618018 ns/iter 0.98
Regex_Period_Asterisk 7.785533482143693 ns/iter 8.00758370535805 ns/iter 0.97
Regex_Group_Period_Asterisk_Group 7.862993303571843 ns/iter 7.792122767856315 ns/iter 1.01
Regex_Period_Plus 7.837021840012947 ns/iter 7.826460937499899 ns/iter 1.00
Regex_Period 7.974022321428313 ns/iter 8.024820312499006 ns/iter 0.99
Regex_Caret_Period_Plus_Dollar 8.013027901786327 ns/iter 22.222493750000183 ns/iter 0.36
Regex_Caret_Group_Period_Plus_Group_Dollar 8.489150854958405 ns/iter 8.137491927956457 ns/iter 1.04
Regex_Caret_Period_Asterisk_Dollar 7.753978794643299 ns/iter 8.022802455356675 ns/iter 0.97
Regex_Caret_Group_Period_Asterisk_Group_Dollar 7.883224518378094 ns/iter 7.876944196428022 ns/iter 1.00
Regex_Caret_X_Hyphen 11.952732812499534 ns/iter 11.588416019695972 ns/iter 1.03
Regex_Period_Md_Dollar 143.64406250000985 ns/iter 142.45619230106436 ns/iter 1.01
Regex_Caret_Slash_Period_Asterisk 11.67749062499901 ns/iter 11.867296428572363 ns/iter 0.98
Regex_Caret_Period_Range_Dollar 8.943330763645985 ns/iter 9.484410937499634 ns/iter 0.94
Regex_Nested_Backtrack 587.8857142856677 ns/iter 590.7661999999618 ns/iter 1.00
Pointer_Object_Traverse 56.08248214285944 ns/iter 56.13882000000104 ns/iter 1.00
Pointer_Object_Try_Traverse 75.84008035713907 ns/iter 76.04763392856334 ns/iter 1.00
Pointer_Push_Back_Pointer_To_Weak_Pointer 189.7341865834237 ns/iter 174.21511434476056 ns/iter 1.09

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark (linux/gcc)

Benchmark suite Current: 08ea8d9 Previous: 30bdbfd Ratio
Pointer_Object_Traverse 52.11801719840574 ns/iter 44.22095085636131 ns/iter 1.18
Pointer_Object_Try_Traverse 22.414314528018476 ns/iter 22.41523260721051 ns/iter 1.00
Pointer_Push_Back_Pointer_To_Weak_Pointer 213.50259850350199 ns/iter 213.4072058434985 ns/iter 1.00
Regex_Lower_S_Or_Upper_S_Asterisk 2.4884240800170185 ns/iter 3.108980096639853 ns/iter 0.80
Regex_Caret_Lower_S_Or_Upper_S_Asterisk_Dollar 2.48587408908152 ns/iter 3.1105155801293787 ns/iter 0.80
Regex_Period_Asterisk 2.488464289802073 ns/iter 3.1101855770313453 ns/iter 0.80
Regex_Group_Period_Asterisk_Group 2.5487888335876563 ns/iter 3.110873547611168 ns/iter 0.82
Regex_Period_Plus 2.4891424237451614 ns/iter 3.111067950855304 ns/iter 0.80
Regex_Period 2.5059946916387283 ns/iter 3.108902804913187 ns/iter 0.81
Regex_Caret_Period_Plus_Dollar 2.486641705289204 ns/iter 3.11563015271816 ns/iter 0.80
Regex_Caret_Group_Period_Plus_Group_Dollar 2.4886072776807415 ns/iter 3.111105390206694 ns/iter 0.80
Regex_Caret_Period_Asterisk_Dollar 2.796502317947934 ns/iter 3.7285986733150622 ns/iter 0.75
Regex_Caret_Group_Period_Asterisk_Group_Dollar 2.799954662955765 ns/iter 3.728761284842226 ns/iter 0.75
Regex_Caret_X_Hyphen 13.053280698598012 ns/iter 12.432857852561543 ns/iter 1.05
Regex_Period_Md_Dollar 89.43861262849947 ns/iter 93.87506188949625 ns/iter 0.95
Regex_Caret_Slash_Period_Asterisk 7.146004550915311 ns/iter 7.152496923695065 ns/iter 1.00
Regex_Caret_Period_Range_Dollar 4.038257818128988 ns/iter 4.043808993874388 ns/iter 1.00
Regex_Nested_Backtrack 817.2460596747886 ns/iter 829.9998964214049 ns/iter 0.98
JSON_Array_Of_Objects_Unique 376.77039072668566 ns/iter 381.31025858999664 ns/iter 0.99
JSON_Parse_1 32592.001259854707 ns/iter 32928.00693695727 ns/iter 0.99
JSON_Fast_Hash_Helm_Chart_Lock 62.46220682271089 ns/iter 62.82525937865529 ns/iter 0.99
JSON_Equality_Helm_Chart_Lock 148.6278505563362 ns/iter 144.1873473920575 ns/iter 1.03

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.