Skip to content

Commit 2bcf935

Browse files
committed
Improve $anchor
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 49d9748 commit 2bcf935

File tree

3 files changed

+58
-71
lines changed

3 files changed

+58
-71
lines changed

content/2020-12/core/anchor.markdown

Lines changed: 56 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,91 +22,79 @@ related:
2222
keyword: $dynamicAnchor
2323
---
2424

25-
The `$anchor` keyword is used to assign a unique identifier to a subschema within its schema resource. This identifier can then be referenced elsewhere using the `$ref` keyword.
25+
The `$anchor` keyword associates a subschema with the given URI fragment
26+
identifier, which can be referenced using the [`$ref`]({{< ref
27+
"2020-12/core/ref" >}}) keyword. The fragment identifier is resolved against
28+
the URI of the schema resource. Therefore, using this keyword to declare the
29+
same anchor more than once within the same schema resource results in an
30+
invalid schema.
2631

27-
* The `$anchor` keyword allows for the creation of plain reusable name fragments that aren't tied to specific structural locations, offering a flexible alternative to using JSON Pointer fragments, which require knowledge of the schema's structure.
28-
* An anchor is resolved against the base URI of its schema resource.
32+
{{<learning-more>}}
2933

30-
## Examples
34+
JSON Schema anchors were inspired by how web browsers [automatically
35+
scroll](https://html.spec.whatwg.org/multipage/browsing-the-web.html#scroll-to-the-fragment-identifier)
36+
to HTML elements given a matching URL fragment identifier.
3137

32-
{{<schema `Schema with a named anchor (identifier)`>}}
33-
{
34-
"$schema": "https://json-schema.org/draft/2020-12/schema",
35-
"$ref": "#string",
36-
"$defs": {
37-
"string": {
38-
"$anchor": "string",
39-
"type": "string"
40-
}
41-
}
42-
}
43-
{{</schema>}}
38+
For example, a company website at `https://example.com` may define a contact
39+
form enclosed in an HTML element such as `<section id="contact">`. When the
40+
user visits the `https://example.com#contact` URL, the web browser will
41+
automatically scroll to the location of such element. Furthermore, the website
42+
can move the contact form to a different location within the same page and the
43+
`https://example.com#contact` URL will continue directing the web browser to
44+
scroll to the correct location.
4445

45-
{{<instance-pass `An instance with a string is valid`>}}
46-
"Hello World!"
47-
{{</instance-pass>}}
46+
{{</learning-more>}}
4847

49-
{{<instance-fail `An instance with a number is invalid`>}}
50-
44
51-
{{</instance-fail>}}
48+
{{<best-practice>}}
5249

53-
{{<schema `Schema with identifiers having absolute URI`>}}
54-
{
55-
"$schema": "https://json-schema.org/draft/2020-12/schema",
56-
"properties": {
57-
"name": { "$ref": "https://example.com/person/name#name" },
58-
"age": { "$ref": "https://example.com/person/age#age" }
59-
},
60-
"required": [ "name", "age" ],
61-
"$defs": {
62-
"name": {
63-
"$id": "https://example.com/person/name",
64-
"$anchor": "name",
65-
"type": "string"
66-
},
67-
"age": {
68-
"$id": "https://example.com/person/age",
69-
"$anchor": "age",
70-
"type": "integer"
71-
}
72-
}
73-
}
74-
{{</schema>}}
50+
This keyword rarely comes up in practice. The only common use in the wild we
51+
are aware of is to mark helpers in a location-independent manner, so the helper
52+
itself can be moved to a different location within the schema without breaking
53+
existing references to it.
7554

76-
{{<instance-pass `An instance adhering to the schema is valid`>}}
77-
{
78-
"name": "John",
79-
"age": 55
80-
}
81-
{{</instance-pass>}}
55+
{{</best-practice>}}
8256

83-
{{<instance-fail `The value of age must be an integer`>}}
84-
{
85-
"name": "foo",
86-
"age": "bar"
87-
}
88-
{{</instance-fail>}}
57+
{{<common-pitfall>}}
8958

90-
{{<schema `Schema with location-independent identifier having base URI change in subschema`>}}
59+
This keyword declares fragment identifiers (a.k.a. anchors) for use _within_
60+
the same schema file or resource. Defining schema files or resources that
61+
reference anchors of _other_ schema files or resources is considered to be an
62+
anti-pattern. If you want to share a subschema across multiple schema files or
63+
resources, that common schema should be a standalone schema file or resource
64+
itself.
65+
66+
{{</common-pitfall>}}
67+
68+
## Examples
69+
70+
{{<schema `A schema that declares a helper schema associated with a location-independent identifier`>}}
9171
{
9272
"$schema": "https://json-schema.org/draft/2020-12/schema",
93-
"$id": "https://example.com/base",
94-
"$ref": "https://example.com/nested#foo",
73+
"$id": "https://example.com/person",
74+
"properties": {
75+
"firstName": {
76+
"$comment": "As a relative reference",
77+
"$ref": "#internal-string"
78+
},
79+
"lastName": {
80+
"$comment": "As an absolute reference",
81+
"$ref": "https://example.com/person#internal-string"
82+
}
83+
},
9584
"$defs": {
96-
"foo": {
97-
"$id": "nested",
98-
"$anchor": "foo",
99-
"type": "integer"
85+
"nonEmptyString": {
86+
"$anchor": "internal-string",
87+
"type": "string",
88+
"minLength": 1
10089
}
10190
}
10291
}
10392
{{</schema>}}
10493

105-
{{<instance-pass `An instance with integer is valid`>}}
106-
99
94+
{{<instance-pass `An object value with non-empty first and last names is valid`>}}
95+
{ "firstName": "John", "lastName": "Doe" }
10796
{{</instance-pass>}}
10897

109-
{{<instance-fail `An instance with boolean is invalid`>}}
110-
true
98+
{{<instance-fail `An object value with empty first and last names is invalid`>}}
99+
{ "firstName": "", "lastName": "" }
111100
{{</instance-fail>}}
112-
- Here the URI Reference of `foo` subschema is resolved to `https://example.com/nested` and the named anchor is used in the URI fragment to reference this subschema.

content/2020-12/core/defs.markdown

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Use this keyword to reduce duplication of internal declarations within a
3232
schema. However, **prefer extracting standalone entities that represent more
3333
than just internal helpers into separate schema files**, and externally
3434
referencing them instead. Otherwise, you will end up with big monolithic
35-
schemas that are challenging to understand and maintain.
35+
schemas that are challenging to understand and maintain.
3636

3737
If you need to resolve external references in advance (for distribution or
3838
analysis), look at the [`jsonschema
@@ -70,7 +70,6 @@ schema should be a standalone schema file or resource itself.
7070
}
7171
{{</schema>}}
7272

73-
7473
{{<instance-pass `An object value with non-empty first and last names is valid`>}}
7574
{ "firstName": "John", "lastName": "Doe" }
7675
{{</instance-pass>}}

layouts/_default/single.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ <h1 class="fw-bold">{{ .Params.keyword }}</h1>
4141
<td class="text-break">
4242
{{ if isset .Params "value" }}
4343
<small>{{ .Params.value | markdownify }}</small>
44-
<small class="d-block mt-2 pt-1 border-top text-muted"><b>Hint:</b> Use the <a href="https://github.com/sourcemeta/jsonschema/blob/main/docs/metaschema.markdown"><code>jsonschema metaschema</code></a> command to catch keywords set to invalid values</small>
44+
<small class="d-block mt-2 pt-1 border-top text-muted"><b>Hint:</b> Use the <a href="https://github.com/sourcemeta/jsonschema/blob/main/docs/metaschema.markdown"><code>jsonschema metaschema</code></a> and <a href="https://github.com/sourcemeta/jsonschema/blob/main/docs/lint.markdown"><code>jsonschema lint</code></a> commands to catch keywords set to invalid values</small>
4545
{{ else }}
4646
{{ errorf "Keyword is missing value description" }}
4747
{{ end }}

0 commit comments

Comments
 (0)