This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 9310405cb6 Updates to @Schema annotation and fix build issue.
9310405cb6 is described below

commit 9310405cb62746b66809a176f933fb167a3d0352
Author: James Bognar <[email protected]>
AuthorDate: Mon Oct 13 13:18:13 2025 -0400

    Updates to @Schema annotation and fix build issue.
---
 .../java/org/apache/juneau/annotation/Schema.java  | 579 ++++++++++++++++++++-
 .../apache/juneau/annotation/SchemaAnnotation.java | 350 ++++++++++++-
 .../org/apache/juneau/httppart/HttpPartSchema.java | 171 +++++-
 juneau-docs/docs/release-notes/9.2.0.md            |  89 ++++
 .../build-overlay/pom.xml                          |   2 +-
 juneau-shaded/juneau-shaded-rest-server/pom.xml    |   7 +-
 .../juneau/httppart/HttpPartSchema_Body_Test.java  | 142 +++++
 .../juneau/jsonschema/JsonSchemaGeneratorTest.java | 228 ++++++++
 .../annotation/SchemaAnnotation_Test.java          | 150 ++++++
 9 files changed, 1703 insertions(+), 15 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Schema.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Schema.java
index 9fdbf0cc67..143fce5309 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Schema.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/Schema.java
@@ -28,13 +28,20 @@ import org.apache.juneau.httppart.*;
 import org.apache.juneau.oapi.*;
 
 /**
- * Swagger schema annotation.
+ * Swagger/OpenAPI/JSON Schema annotation.
  *
  * <p>
  * The Schema Object allows the definition of input and output data types.
  * These types can be objects, but also primitives and arrays.
- * This object is based on the JSON Schema Specification Draft 4 and uses a 
predefined subset of it.
- * On top of this subset, there are extensions provided by this specification 
to allow for more complete documentation.
+ * This annotation is based on the JSON Schema Specification and supports 
multiple versions:
+ * <ul>
+ *     <li><b>JSON Schema Draft 04</b> - Core properties (via Swagger 
2.0/OpenAPI 3.0)
+ *     <li><b>JSON Schema Draft 2020-12</b> - Extended properties and updated 
semantics
+ * </ul>
+ *
+ * <p>
+ * For backward compatibility, all Swagger 2.0 and OpenAPI 3.0 properties are 
supported.
+ * New Draft 2020-12 properties are opt-in and can be used alongside existing 
properties.
  *
  * <p>
  * Used to populate the auto-generated Swagger documentation and UI for 
server-side <ja>@Rest</ja>-annotated classes.
@@ -66,10 +73,23 @@ import org.apache.juneau.oapi.*;
  *             )
  *     )
  * </p>
+ * <p class='bjava'>
+ *     <jc>// Using Draft 2020-12 properties</jc>
+ *     <ja>@Schema</ja>(
+ *             type=<js>"number"</js>,
+ *             exclusiveMaximumValue=<js>"100"</js>,  <jc>// Draft 2020-12 
numeric format</jc>
+ *             examples={<js>"50"</js>, <js>"75"</js>, <js>"99"</js>},  <jc>// 
Draft 2020-12 property</jc>
+ *             deprecatedProperty=<jk>true</jk>  <jc>// Draft 2020-12 
property</jc>
+ *     )
+ * </p>
  *
  * <h5 class='section'>See Also:</h5><ul>
  *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2";>juneau-bean-swagger-v2</a>
- *     <li class='extlink'><a class="doclink" 
href="https://swagger.io/specification/v2#schemaObject";>Swagger Schema 
Object</a>
+ *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
+ *     <li class='extlink'><a class="doclink" 
href="https://swagger.io/specification/v2#schemaObject";>Swagger 2.0 Schema 
Object</a>
+ *     <li class='extlink'><a class="doclink" 
href="https://spec.openapis.org/oas/v3.0.3#schema-object";>OpenAPI 3.0 Schema 
Object</a>
+ *     <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html";>JSON Schema 
Draft 2020-12 Core</a>
+ *     <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html";>JSON 
Schema Draft 2020-12 Validation</a>
 
  * </ul>
  */
@@ -384,6 +404,13 @@ public @interface Schema {
         * Only allowed for the following types: <js>"integer"</js>, 
<js>"number"</js>.
         * <br>If <jk>true</jk>, must be accompanied with <c>maximum</c>.
         *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              <b>Deprecated in JSON Schema Draft 2020-12:</b> This 
boolean format is from Swagger 2.0/OpenAPI 3.0 and JSON Schema Draft 04.
+        *              Consider using {@link #exclusiveMaximumValue()} for 
Draft 2020-12 compliance, which uses a numeric value instead.
+        *              For backward compatibility, if {@link 
#exclusiveMaximumValue()} is set, it takes precedence over this property.
+        * </ul>
+        *
         * <h5 class='section'>Used for:</h5>
         * <ul class='spaced-list'>
         *      <li>
@@ -395,7 +422,9 @@ public @interface Schema {
         * </ul>
         *
         * @return The annotation value.
+        * @deprecated Use {@link #exclusiveMaximumValue()} for JSON Schema 
Draft 2020-12 compliance.
         */
+       @Deprecated
        boolean exclusiveMaximum() default false;
 
        /**
@@ -413,6 +442,13 @@ public @interface Schema {
         * Only allowed for the following types: <js>"integer"</js>, 
<js>"number"</js>.
         * <br>If <jk>true</jk>, must be accompanied with <c>minimum</c>.
         *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              <b>Deprecated in JSON Schema Draft 2020-12:</b> This 
boolean format is from Swagger 2.0/OpenAPI 3.0 and JSON Schema Draft 04.
+        *              Consider using {@link #exclusiveMinimumValue()} for 
Draft 2020-12 compliance, which uses a numeric value instead.
+        *              For backward compatibility, if {@link 
#exclusiveMinimumValue()} is set, it takes precedence over this property.
+        * </ul>
+        *
         * <h5 class='section'>Used for:</h5>
         * <ul class='spaced-list'>
         *      <li>
@@ -424,7 +460,9 @@ public @interface Schema {
         * </ul>
         *
         * @return The annotation value.
+        * @deprecated Use {@link #exclusiveMinimumValue()} for JSON Schema 
Draft 2020-12 compliance.
         */
+       @Deprecated
        boolean exclusiveMinimum() default false;
 
        /**
@@ -1239,4 +1277,537 @@ public @interface Schema {
         * @return The annotation value.
         */
        String[] xml() default {};
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // JSON Schema Draft 2020-12 properties
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * <mk>const</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The value of this keyword MAY be of any type, including null.
+        * An instance validates successfully against this keyword if its value 
is equal to the value of this keyword.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// A constant string value</jc>
+        *      <ja>@Schema</ja>(_const=<js>"fixed-value"</js>)
+        *      <jk>public</jk> String getStatus() {...}
+        * </p>
+        * <p class='bjava'>
+        *      <jc>// A constant numeric value</jc>
+        *      <ja>@Schema</ja>(_const=<js>"42"</js>)
+        *      <jk>public int</jk> getMagicNumber() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side schema-based parsing validation.
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        *      <li>
+        *              Client-side schema-based serializing validation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.1.3";>JSON
 Schema Validation § 6.1.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] _const() default {};
+
+       /**
+        * <mk>examples</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The value of this keyword MUST be an array.
+        * There are no restrictions on the values within the array.
+        * When multiple examples are applicable, an array of examples can be 
used.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// Multiple examples of valid values</jc>
+        *      <ja>@Schema</ja>(
+        *              type=<js>"string"</js>,
+        *              examples={<js>"red"</js>, <js>"green"</js>, 
<js>"blue"</js>}
+        *      )
+        *      <jk>public</jk> String getColor() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.9.5";>JSON
 Schema Validation § 9.5</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] examples() default {};
+
+       /**
+        * <mk>$comment</mk> field of the JSON Schema.
+        *
+        * <p>
+        * This keyword reserves a location for comments from schema authors to 
readers or maintainers of the schema.
+        * The value of this keyword MUST be a string.
+        * Implementations MUST NOT present this string to end users.
+        * Tools for editing schemas SHOULD support displaying and editing this 
keyword.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <ja>@Schema</ja>(
+        *              type=<js>"string"</js>,
+        *              $comment=<js>"This field is deprecated but maintained 
for backward compatibility"</js>
+        *      )
+        *      <jk>public</jk> String getLegacyField() {...}
+        * </p>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.8.3";>JSON
 Schema Core § 8.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] $comment() default {};
+
+       /**
+        * <mk>deprecated</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The value of this keyword MUST be a boolean.
+        * When true, applications SHOULD refrain from usage of the declared 
property.
+        * It may mean the property is going to be removed in the future.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <ja>@Schema</ja>(deprecated=<jk>true</jk>)
+        *      <ja>@Deprecated</ja>
+        *      <jk>public</jk> String getOldMethod() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.9.3";>JSON
 Schema Validation § 9.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       boolean deprecatedProperty() default false;
+
+       /**
+        * <mk>exclusiveMaximum</mk> field of the JSON Schema (Draft 2020-12 
numeric value).
+        *
+        * <p>
+        * The value of this keyword MUST be a number.
+        * The instance is valid if it is strictly less than (not equal to) the 
value specified by this keyword.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that replaces the 
boolean {@link #exclusiveMaximum()}.
+        * For backward compatibility, both properties are supported.
+        * If this property is specified, it takes precedence over the boolean 
version.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// A number that must be strictly less than 100</jc>
+        *      <ja>@Schema</ja>(
+        *              type=<js>"number"</js>,
+        *              exclusiveMaximumValue=<js>"100"</js>
+        *      )
+        *      <jk>public</jk> Double getPercentage() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side schema-based parsing validation.
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        *      <li>
+        *              Client-side schema-based serializing validation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.3";>JSON
 Schema Validation § 6.2.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String exclusiveMaximumValue() default "";
+
+       /**
+        * <mk>exclusiveMinimum</mk> field of the JSON Schema (Draft 2020-12 
numeric value).
+        *
+        * <p>
+        * The value of this keyword MUST be a number.
+        * The instance is valid if it is strictly greater than (not equal to) 
the value specified by this keyword.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that replaces the 
boolean {@link #exclusiveMinimum()}.
+        * For backward compatibility, both properties are supported.
+        * If this property is specified, it takes precedence over the boolean 
version.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// A number that must be strictly greater than 0</jc>
+        *      <ja>@Schema</ja>(
+        *              type=<js>"number"</js>,
+        *              exclusiveMinimumValue=<js>"0"</js>
+        *      )
+        *      <jk>public</jk> Double getPositiveNumber() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side schema-based parsing validation.
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        *      <li>
+        *              Client-side schema-based serializing validation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.5";>JSON
 Schema Validation § 6.2.5</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String exclusiveMinimumValue() default "";
+
+       /**
+        * <mk>contentMediaType</mk> field of the JSON Schema.
+        *
+        * <p>
+        * If the instance is a string, this property defines the MIME type of 
the contents of the string.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// A string containing JSON data</jc>
+        *      <ja>@Schema</ja>(
+        *              type=<js>"string"</js>,
+        *              contentMediaType=<js>"application/json"</js>
+        *      )
+        *      <jk>public</jk> String getJsonData() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.8.3";>JSON
 Schema Validation § 8.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String contentMediaType() default "";
+
+       /**
+        * <mk>contentEncoding</mk> field of the JSON Schema.
+        *
+        * <p>
+        * If the instance is a string, this property defines the encoding used 
to store the contents.
+        * Common values: "base64", "quoted-printable", "7bit", "8bit", "binary"
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <jc>// A base64-encoded binary string</jc>
+        *      <ja>@Schema</ja>(
+        *              type=<js>"string"</js>,
+        *              contentEncoding=<js>"base64"</js>,
+        *              contentMediaType=<js>"image/png"</js>
+        *      )
+        *      <jk>public</jk> String getImageData() {...}
+        * </p>
+        *
+        * <h5 class='section'>Used for:</h5>
+        * <ul class='spaced-list'>
+        *      <li>
+        *              Server-side generated Swagger documentation.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.8.3";>JSON
 Schema Validation § 8.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String contentEncoding() default "";
+
+       /**
+        * <mk>prefixItems</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The value of "prefixItems" MUST be a non-empty array of valid JSON 
Schemas.
+        * Validation succeeds if each element of the instance validates 
against the schema at the same position, if any.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that provides tuple 
validation for arrays.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.3.1.1";>JSON
 Schema Core § 10.3.1.1</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] prefixItems() default {};
+
+       /**
+        * <mk>unevaluatedItems</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The value of "unevaluatedItems" MUST be a valid JSON Schema.
+        * This schema applies to array items that were not evaluated by 
"items", "prefixItems", "contains", etc.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.11.3";>JSON
 Schema Core § 11.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] unevaluatedItems() default {};
+
+       /**
+        * <mk>unevaluatedProperties</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The value of "unevaluatedProperties" MUST be a valid JSON Schema.
+        * This schema applies to object properties that were not evaluated by 
"properties", "patternProperties", etc.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.11.3";>JSON
 Schema Core § 11.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] unevaluatedProperties() default {};
+
+       /**
+        * <mk>dependentSchemas</mk> field of the JSON Schema.
+        *
+        * <p>
+        * This keyword specifies subschemas that are evaluated if the instance 
is an object and contains a certain property.
+        * The value of this keyword MUST be an object where each value is a 
valid JSON Schema.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.2.2.4";>JSON
 Schema Core § 10.2.2.4</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] dependentSchemas() default {};
+
+       /**
+        * <mk>dependentRequired</mk> field of the JSON Schema.
+        *
+        * <p>
+        * This keyword specifies properties that are required if a specific 
other property is present.
+        * The value of this keyword MUST be an object where each value is an 
array of strings.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.5.4";>JSON
 Schema Validation § 6.5.4</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] dependentRequired() default {};
+
+       /**
+        * <mk>if</mk> field of the JSON Schema.
+        *
+        * <p>
+        * This keyword's value MUST be a valid JSON Schema.
+        * This validation outcome of this keyword's subschema has no direct 
effect on the overall validation result.
+        * Rather, it controls which of the "then" or "else" keywords are 
evaluated.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that provides 
conditional schema validation.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.2.2.3";>JSON
 Schema Core § 10.2.2.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] _if() default {};
+
+       /**
+        * <mk>then</mk> field of the JSON Schema.
+        *
+        * <p>
+        * This keyword's value MUST be a valid JSON Schema.
+        * When "if" is present, and the instance successfully validates 
against its subschema,
+        * then validation succeeds against this keyword if the instance also 
successfully validates against this keyword's subschema.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that provides 
conditional schema validation.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.2.2.3";>JSON
 Schema Core § 10.2.2.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] _then() default {};
+
+       /**
+        * <mk>else</mk> field of the JSON Schema.
+        *
+        * <p>
+        * This keyword's value MUST be a valid JSON Schema.
+        * When "if" is present, and the instance fails to validate against its 
subschema,
+        * then validation succeeds against this keyword if the instance 
successfully validates against this keyword's subschema.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that provides 
conditional schema validation.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.10.2.2.3";>JSON
 Schema Core § 10.2.2.3</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] _else() default {};
+
+       /**
+        * <mk>$defs</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The "$defs" keyword reserves a location for schema authors to inline 
re-usable JSON Schemas into a more general schema.
+        * The value of this keyword MUST be an object. Each value of this 
object MUST be a valid JSON Schema.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property that replaces the older 
"definitions" keyword.
+        *
+        * <h5 class='section'>Notes:</h5><ul>
+        *      <li class='note'>
+        *              The format is a <a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauBeanJsonSchema";>juneau-bean-jsonschema</a>
 object.
+        *              <br>Multiple lines are concatenated with newlines.
+        * </ul>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.8.2.4";>JSON
 Schema Core § 8.2.4</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] $defs() default {};
+
+       /**
+        * <mk>$id</mk> field of the JSON Schema.
+        *
+        * <p>
+        * The "$id" keyword defines a URI for the schema, and the base URI 
that other URI references within the schema are resolved against.
+        *
+        * <p>
+        * This is a JSON Schema Draft 2020-12 property.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bjava'>
+        *      <ja>@Schema</ja>(
+        *              $id=<js>"https://example.com/schemas/person.json";</js>,
+        *              type=<js>"object"</js>
+        *      )
+        *      <jk>public class</jk> Person {...}
+        * </p>
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='extlink'><a class="doclink" 
href="https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.8.2.1";>JSON
 Schema Core § 8.2.1</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String $id() default "";
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/SchemaAnnotation.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/SchemaAnnotation.java
index 63b3e28729..b966792138 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/SchemaAnnotation.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/annotation/SchemaAnnotation.java
@@ -112,8 +112,14 @@ public class SchemaAnnotation {
                        .appendIf(ne, "discriminator", a.discriminator())
                        .appendIf(ne, "description", joinnl(a.description(), 
a.d()))
                        .appendFirst(nec, "enum", parseSet(a._enum()), 
parseSet(a.e()))
-                       .appendIf(nf, "exclusiveMaximum", a.exclusiveMaximum() 
|| a.emax())
-                       .appendIf(nf, "exclusiveMinimum", a.exclusiveMinimum() 
|| a.emin())
+                       // Handle exclusiveMaximum with Draft 2020-12 fallback
+                       .appendIf(ne, "exclusiveMaximum", 
+                               ne.test(a.exclusiveMaximumValue()) ? 
a.exclusiveMaximumValue() : 
+                               (a.exclusiveMaximum() || a.emax()) ? "true" : 
null)
+                       // Handle exclusiveMinimum with Draft 2020-12 fallback
+                       .appendIf(ne, "exclusiveMinimum", 
+                               ne.test(a.exclusiveMinimumValue()) ? 
a.exclusiveMinimumValue() : 
+                               (a.exclusiveMinimum() || a.emin()) ? "true" : 
null)
                        .appendIf(nem, "externalDocs", 
ExternalDocsAnnotation.merge(m.getMap("externalDocs"), a.externalDocs()))
                        .appendFirst(ne, "format", a.format(), a.f())
                        .appendIf(ne, "ignore", a.ignore() ? "true" : null)
@@ -136,6 +142,23 @@ public class SchemaAnnotation {
                        .appendIf(nf, "uniqueItems", a.uniqueItems() || a.ui())
                        .appendIf(ne, "xml", joinnl(a.xml()))
                        .appendIf(ne, "$ref", a.$ref())
+                       // JSON Schema Draft 2020-12 properties
+                       .appendIf(ne, "const", joinnl(a._const()))
+                       .appendIf(nec, "examples", a.examples().length == 0 ? 
null : Arrays.asList(a.examples()))
+                       .appendIf(ne, "$comment", joinnl(a.$comment()))
+                       .appendIf(nf, "deprecated", a.deprecatedProperty())
+                       .appendIf(ne, "contentMediaType", a.contentMediaType())
+                       .appendIf(ne, "contentEncoding", a.contentEncoding())
+                       .appendIf(ne, "prefixItems", joinnl(a.prefixItems()))
+                       .appendIf(ne, "unevaluatedItems", 
joinnl(a.unevaluatedItems()))
+                       .appendIf(ne, "unevaluatedProperties", 
joinnl(a.unevaluatedProperties()))
+                       .appendIf(ne, "dependentSchemas", 
joinnl(a.dependentSchemas()))
+                       .appendIf(ne, "dependentRequired", 
joinnl(a.dependentRequired()))
+                       .appendIf(ne, "if", joinnl(a._if()))
+                       .appendIf(ne, "then", joinnl(a._then()))
+                       .appendIf(ne, "else", joinnl(a._else()))
+                       .appendIf(ne, "$defs", joinnl(a.$defs()))
+                       .appendIf(ne, "$id", a.$id())
                ;
        }
 
@@ -188,6 +211,10 @@ public class SchemaAnnotation {
                long maxi=-1, maxItems=-1, maxl=-1, maxLength=-1, maxp=-1, 
maxProperties=-1, mini=-1, minItems=-1, minl=-1, minLength=-1, minp=-1, 
minProperties=-1;
                String $ref="", cf="", collectionFormat="", discriminator="", 
f="", format="", max="", maximum="", min="", minimum="", mo="", multipleOf="", 
p="", pattern="", t="", title="", type="";
                String[] _default={}, _enum={}, additionalProperties={}, 
allOf={}, d={}, description={}, df={}, e={}, properties={}, value={}, xml={};
+               // JSON Schema Draft 2020-12 properties
+               boolean deprecatedProperty;
+               String $id="", contentMediaType="", contentEncoding="", 
exclusiveMaximumValue="", exclusiveMinimumValue="";
+               String[] _const={}, examples={}, $comment={}, prefixItems={}, 
unevaluatedItems={}, unevaluatedProperties={}, dependentSchemas={}, 
dependentRequired={}, _if={}, _then={}, _else={}, $defs={};
 
                /**
                 * Constructor.
@@ -810,6 +837,208 @@ public class SchemaAnnotation {
                        return this;
                }
 
+               // 
-----------------------------------------------------------------------------------------------------------------
+               // JSON Schema Draft 2020-12 property setters
+               // 
-----------------------------------------------------------------------------------------------------------------
+
+               /**
+                * Sets the {@link Schema#_const} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder _const(String...value) {
+                       this._const = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#examples} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder examples(String...value) {
+                       this.examples = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#$comment} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder $comment(String...value) {
+                       this.$comment = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#deprecatedProperty} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder deprecatedProperty(boolean value) {
+                       this.deprecatedProperty = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#exclusiveMaximumValue} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder exclusiveMaximumValue(String value) {
+                       this.exclusiveMaximumValue = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#exclusiveMinimumValue} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder exclusiveMinimumValue(String value) {
+                       this.exclusiveMinimumValue = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#contentMediaType} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder contentMediaType(String value) {
+                       this.contentMediaType = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#contentEncoding} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder contentEncoding(String value) {
+                       this.contentEncoding = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#prefixItems} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder prefixItems(String...value) {
+                       this.prefixItems = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#unevaluatedItems} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder unevaluatedItems(String...value) {
+                       this.unevaluatedItems = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#unevaluatedProperties} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder unevaluatedProperties(String...value) {
+                       this.unevaluatedProperties = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#dependentSchemas} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder dependentSchemas(String...value) {
+                       this.dependentSchemas = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#dependentRequired} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder dependentRequired(String...value) {
+                       this.dependentRequired = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#_if} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder _if(String...value) {
+                       this._if = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#_then} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder _then(String...value) {
+                       this._then = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#_else} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder _else(String...value) {
+                       this._else = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#$defs} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder $defs(String...value) {
+                       this.$defs = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Schema#$id} property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder $id(String value) {
+                       this.$id = value;
+                       return this;
+               }
+
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
@@ -824,6 +1053,10 @@ public class SchemaAnnotation {
                private final long maxLength, maxl, minLength, minl, maxItems, 
maxi, minItems, mini, maxProperties, maxp, minProperties, minp;
                private final String $ref, format, f, title, multipleOf, mo, 
maximum, max, minimum, min, pattern, p, type, t, collectionFormat, cf, 
discriminator;
                private final String[] d, _default, df, _enum, e, allOf, 
properties, additionalProperties, xml;
+               // JSON Schema Draft 2020-12 fields
+               private final boolean deprecatedProperty;
+               private final String $id, contentMediaType, contentEncoding, 
exclusiveMaximumValue, exclusiveMinimumValue;
+               private final String[] _const, examples, $comment, prefixItems, 
unevaluatedItems, unevaluatedProperties, dependentSchemas, dependentRequired, 
_if, _then, _else, $defs;
 
                Impl(Builder b) {
                        super(b);
@@ -882,6 +1115,25 @@ public class SchemaAnnotation {
                        this.ui = b.ui;
                        this.uniqueItems = b.uniqueItems;
                        this.xml = copyOf(b.xml);
+                       // JSON Schema Draft 2020-12 fields
+                       this.deprecatedProperty = b.deprecatedProperty;
+                       this.$id = b.$id;
+                       this.contentMediaType = b.contentMediaType;
+                       this.contentEncoding = b.contentEncoding;
+                       this.exclusiveMaximumValue = b.exclusiveMaximumValue;
+                       this.exclusiveMinimumValue = b.exclusiveMinimumValue;
+                       this._const = copyOf(b._const);
+                       this.examples = copyOf(b.examples);
+                       this.$comment = copyOf(b.$comment);
+                       this.prefixItems = copyOf(b.prefixItems);
+                       this.unevaluatedItems = copyOf(b.unevaluatedItems);
+                       this.unevaluatedProperties = 
copyOf(b.unevaluatedProperties);
+                       this.dependentSchemas = copyOf(b.dependentSchemas);
+                       this.dependentRequired = copyOf(b.dependentRequired);
+                       this._if = copyOf(b._if);
+                       this._then = copyOf(b._then);
+                       this._else = copyOf(b._else);
+                       this.$defs = copyOf(b.$defs);
                        postConstruct();
                }
 
@@ -1159,6 +1411,100 @@ public class SchemaAnnotation {
                public String[] xml() {
                        return xml;
                }
+
+               // 
-----------------------------------------------------------------------------------------------------------------
+               // JSON Schema Draft 2020-12 property getters
+               // 
-----------------------------------------------------------------------------------------------------------------
+
+               @Override /* Schema */
+               public String[] _const() {
+                       return _const;
+               }
+
+               @Override /* Schema */
+               public String[] examples() {
+                       return examples;
+               }
+
+               @Override /* Schema */
+               public String[] $comment() {
+                       return $comment;
+               }
+
+               @Override /* Schema */
+               public boolean deprecatedProperty() {
+                       return deprecatedProperty;
+               }
+
+               @Override /* Schema */
+               public String exclusiveMaximumValue() {
+                       return exclusiveMaximumValue;
+               }
+
+               @Override /* Schema */
+               public String exclusiveMinimumValue() {
+                       return exclusiveMinimumValue;
+               }
+
+               @Override /* Schema */
+               public String contentMediaType() {
+                       return contentMediaType;
+               }
+
+               @Override /* Schema */
+               public String contentEncoding() {
+                       return contentEncoding;
+               }
+
+               @Override /* Schema */
+               public String[] prefixItems() {
+                       return prefixItems;
+               }
+
+               @Override /* Schema */
+               public String[] unevaluatedItems() {
+                       return unevaluatedItems;
+               }
+
+               @Override /* Schema */
+               public String[] unevaluatedProperties() {
+                       return unevaluatedProperties;
+               }
+
+               @Override /* Schema */
+               public String[] dependentSchemas() {
+                       return dependentSchemas;
+               }
+
+               @Override /* Schema */
+               public String[] dependentRequired() {
+                       return dependentRequired;
+               }
+
+               @Override /* Schema */
+               public String[] _if() {
+                       return _if;
+               }
+
+               @Override /* Schema */
+               public String[] _then() {
+                       return _then;
+               }
+
+               @Override /* Schema */
+               public String[] _else() {
+                       return _else;
+               }
+
+               @Override /* Schema */
+               public String[] $defs() {
+                       return $defs;
+               }
+
+               @Override /* Schema */
+               public String $id() {
+                       return $id;
+               }
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
index c5593066f1..f1b2a7af10 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
@@ -654,6 +654,11 @@ public class HttpPartSchema {
                boolean noValidate;
                Class<? extends HttpPartParser> parser;
                Class<? extends HttpPartSerializer> serializer;
+               // JSON Schema Draft 2020-12 properties
+               String _const;
+               String[] examples;
+               Boolean deprecated;
+               Number exclusiveMaximumValue, exclusiveMinimumValue;
 
                /**
                 * Instantiates a new {@link HttpPartSchema} object based on 
the configuration of this builder.
@@ -847,8 +852,23 @@ public class HttpPartSchema {
                        
additionalProperties(HttpPartSchema.toJsonMap(a.additionalProperties()));
                        allowEmptyValue(a.allowEmptyValue() || a.aev());
                        collectionFormat(firstNonEmpty(a.collectionFormat(), 
a.cf()));
-                       exclusiveMaximum(a.exclusiveMaximum() || a.emax());
-                       exclusiveMinimum(a.exclusiveMinimum() || a.emin());
+
+                       // Handle exclusiveMaximum with fallback from Draft 
2020-12 to Draft 04
+                       String exMaxVal = a.exclusiveMaximumValue();
+                       if (isNotEmpty(exMaxVal)) {
+                               exclusiveMaximumValue(toNumber(exMaxVal));
+                       } else if (a.exclusiveMaximum() || a.emax()) {
+                               exclusiveMaximum(true);
+                       }
+
+                       // Handle exclusiveMinimum with fallback from Draft 
2020-12 to Draft 04
+                       String exMinVal = a.exclusiveMinimumValue();
+                       if (isNotEmpty(exMinVal)) {
+                               exclusiveMinimumValue(toNumber(exMinVal));
+                       } else if (a.exclusiveMinimum() || a.emin()) {
+                               exclusiveMinimum(true);
+                       }
+
                        format(firstNonEmpty(a.format(), a.f()));
                        items(a.items());
                        maximum(toNumber(a.maximum(), a.max()));
@@ -866,6 +886,12 @@ public class HttpPartSchema {
                        skipIfEmpty(a.skipIfEmpty() || a.sie());
                        type(firstNonEmpty(a.type(), a.t()));
                        uniqueItems(a.uniqueItems() || a.ui());
+
+                       // JSON Schema Draft 2020-12 properties
+                       _const(joinnlOrNull(a._const()));
+                       examples(a.examples());
+                       deprecated(a.deprecatedProperty());
+
                        return this;
                }
 
@@ -3145,6 +3171,102 @@ public class HttpPartSchema {
                        return this;
                }
 
+               // 
-----------------------------------------------------------------------------------------------------------------
+               // JSON Schema Draft 2020-12 property setters
+               // 
-----------------------------------------------------------------------------------------------------------------
+
+               /**
+                * <mk>const</mk> field (JSON Schema Draft 2020-12).
+                *
+                * <p>
+                * Defines a constant value for this schema.
+                * The instance must be equal to this value to validate.
+                *
+                * @param value
+                *      The new value for this property.
+                * @return This object.
+                */
+               public Builder _const(String value) {
+                       this._const = value;
+                       return this;
+               }
+
+               /**
+                * <mk>examples</mk> field (JSON Schema Draft 2020-12).
+                *
+                * <p>
+                * An array of example values.
+                * This is used for documentation purposes only and does not 
affect validation.
+                *
+                * @param value
+                *      The new value for this property.
+                * @return This object.
+                */
+               public Builder examples(String... value) {
+                       this.examples = value;
+                       return this;
+               }
+
+               /**
+                * <mk>deprecated</mk> field (JSON Schema Draft 2020-12).
+                *
+                * <p>
+                * Indicates that applications should refrain from usage of 
this property.
+                * This is used for documentation purposes only and does not 
affect validation.
+                *
+                * @param value
+                *      The new value for this property.
+                * @return This object.
+                */
+               public Builder deprecated(Boolean value) {
+                       deprecated = resolve(value, deprecated);
+                       return this;
+               }
+
+               /**
+                * <mk>exclusiveMaximum</mk> field with numeric value (JSON 
Schema Draft 2020-12).
+                *
+                * <p>
+                * Defines the exclusive maximum value for numeric types.
+                * The instance is valid if it is strictly less than (not equal 
to) this value.
+                *
+                * <p>
+                * This is the Draft 2020-12 version that uses a numeric value 
instead of a boolean flag.
+                * If this is set, it takes precedence over the boolean {@link 
#exclusiveMaximum(Boolean)} property.
+                *
+                * @param value
+                *      The new value for this property.
+                * @return This object.
+                */
+               public Builder exclusiveMaximumValue(Number value) {
+                       this.exclusiveMaximumValue = value;
+                       return this;
+               }
+
+               /**
+                * <mk>exclusiveMinimum</mk> field with numeric value (JSON 
Schema Draft 2020-12).
+                *
+                * <p>
+                * Defines the exclusive minimum value for numeric types.
+                * The instance is valid if it is strictly greater than (not 
equal to) this value.
+                *
+                * <p>
+                * This is the Draft 2020-12 version that uses a numeric value 
instead of a boolean flag.
+                * If this is set, it takes precedence over the boolean {@link 
#exclusiveMinimum(Boolean)} property.
+                *
+                * @param value
+                *      The new value for this property.
+                * @return This object.
+                */
+               public Builder exclusiveMinimumValue(Number value) {
+                       this.exclusiveMinimumValue = value;
+                       return this;
+               }
+
+               // 
-----------------------------------------------------------------------------------------------------------------
+               // Other
+               // 
-----------------------------------------------------------------------------------------------------------------
+
                /**
                 * Disables Swagger schema usage validation checking.
                 *
@@ -3227,6 +3349,11 @@ public class HttpPartSchema {
        final Class<? extends HttpPartParser> parser;
        final Class<? extends HttpPartSerializer> serializer;
        final ClassMeta<?> parsedType;
+       // JSON Schema Draft 2020-12 fields
+       final String _const;
+       final String[] examples;
+       final boolean deprecated;
+       final Number exclusiveMaximumValue, exclusiveMinimumValue;
 
        HttpPartSchema(Builder b) {
                this.name = b.name;
@@ -3256,6 +3383,12 @@ public class HttpPartSchema {
                this.minProperties = b.minProperties;
                this.parser = b.parser;
                this.serializer = b.serializer;
+               // JSON Schema Draft 2020-12 fields
+               this._const = b._const;
+               this.examples = b.examples;
+               this.deprecated = resolve(b.deprecated);
+               this.exclusiveMaximumValue = b.exclusiveMaximumValue;
+               this.exclusiveMinimumValue = b.exclusiveMinimumValue;
 
                // Calculate parse type
                Class<?> parsedType = Object.class;
@@ -3766,6 +3899,8 @@ public class HttpPartSchema {
                if (in != null) {
                        if (! isValidAllowEmpty(in))
                                throw new SchemaValidationException("Empty 
value not allowed.");
+                       if (! isValidConst(in))
+                               throw new SchemaValidationException("Value does 
not match constant.  Must be: {0}", _const);
                        if (! isValidPattern(in))
                                throw new SchemaValidationException("Value does 
not match expected pattern.  Must match pattern: {0}", pattern.pattern());
                        if (! isValidEnum(in))
@@ -3889,6 +4024,20 @@ public class HttpPartSchema {
        }
 
        private boolean isValidMinimum(Number x) {
+               // Check Draft 2020-12 exclusiveMinimumValue first (takes 
precedence)
+               if (exclusiveMinimumValue != null) {
+                       if (x instanceof Integer || x instanceof AtomicInteger)
+                               return x.intValue() > 
exclusiveMinimumValue.intValue();
+                       if (x instanceof Short || x instanceof Byte)
+                               return x.shortValue() > 
exclusiveMinimumValue.shortValue();
+                       if (x instanceof Long || x instanceof AtomicLong || x 
instanceof BigInteger)
+                               return x.longValue() > 
exclusiveMinimumValue.longValue();
+                       if (x instanceof Float)
+                               return x.floatValue() > 
exclusiveMinimumValue.floatValue();
+                       if (x instanceof Double || x instanceof BigDecimal)
+                               return x.doubleValue() > 
exclusiveMinimumValue.doubleValue();
+               }
+               // Fall back to Draft 04 boolean exclusiveMinimum with minimum
                if (x instanceof Integer || x instanceof AtomicInteger)
                        return minimum == null || x.intValue() > 
minimum.intValue() || (x.intValue() == minimum.intValue() && (! 
exclusiveMinimum));
                if (x instanceof Short || x instanceof Byte)
@@ -3903,6 +4052,20 @@ public class HttpPartSchema {
        }
 
        private boolean isValidMaximum(Number x) {
+               // Check Draft 2020-12 exclusiveMaximumValue first (takes 
precedence)
+               if (exclusiveMaximumValue != null) {
+                       if (x instanceof Integer || x instanceof AtomicInteger)
+                               return x.intValue() < 
exclusiveMaximumValue.intValue();
+                       if (x instanceof Short || x instanceof Byte)
+                               return x.shortValue() < 
exclusiveMaximumValue.shortValue();
+                       if (x instanceof Long || x instanceof AtomicLong || x 
instanceof BigInteger)
+                               return x.longValue() < 
exclusiveMaximumValue.longValue();
+                       if (x instanceof Float)
+                               return x.floatValue() < 
exclusiveMaximumValue.floatValue();
+                       if (x instanceof Double || x instanceof BigDecimal)
+                               return x.doubleValue() < 
exclusiveMaximumValue.doubleValue();
+               }
+               // Fall back to Draft 04 boolean exclusiveMaximum with maximum
                if (x instanceof Integer || x instanceof AtomicInteger)
                        return maximum == null || x.intValue() < 
maximum.intValue() || (x.intValue() == maximum.intValue() && (! 
exclusiveMaximum));
                if (x instanceof Short || x instanceof Byte)
@@ -3934,6 +4097,10 @@ public class HttpPartSchema {
                return allowEmptyValue || Utils.isNotEmpty(x);
        }
 
+       private boolean isValidConst(String x) {
+               return _const == null || _const.equals(x);
+       }
+
        private boolean isValidPattern(String x) {
                return pattern == null || pattern.matcher(x).matches();
        }
diff --git a/juneau-docs/docs/release-notes/9.2.0.md 
b/juneau-docs/docs/release-notes/9.2.0.md
index 0351178875..61bdca01d8 100644
--- a/juneau-docs/docs/release-notes/9.2.0.md
+++ b/juneau-docs/docs/release-notes/9.2.0.md
@@ -16,6 +16,7 @@ Juneau 9.2.0 is a minor release focused on enhancements and 
bug fixes.
 Major changes include:
 
 - **New Module**: Introduced `juneau-shaded` with five shaded (uber) JAR 
artifacts for simplified dependency management, especially useful for Bazel
+- **@Schema Annotation** upgraded to JSON Schema Draft 2020-12 with 18 new 
properties, while maintaining full backward compatibility with Draft 04
 - JSON Schema beans upgraded to Draft 2020-12 specification with backward 
compatibility for Draft 04
 - Comprehensive enhancements to HTML5 beans with improved javadocs and 
`HtmlBuilder` integration
 - Standardized license headers across all Java files
@@ -131,6 +132,78 @@ Major changes include:
 
 ### juneau-marshall
 
+#### @Schema Annotation - JSON Schema Draft 2020-12 Support
+
+- **Draft 2020-12 Compliance**: The `@Schema` annotation has been upgraded to 
support JSON Schema Draft 2020-12 while maintaining full backward compatibility 
with Draft 04 (used by Swagger 2.0 and OpenAPI 3.0).
+
+  **New Draft 2020-12 Properties**:
+  - `$id()` - Schema URI identifier (e.g., 
`"https://example.com/schemas/user"`)
+  - `_const()` - Constant value validation (value must exactly match)
+  - `examples()` - Array of example values for documentation
+  - `$comment()` - Comments for schema authors (not for end users)
+  - `deprecatedProperty()` - Boolean flag to mark schema/property as deprecated
+  - `exclusiveMaximumValue()` / `exclusiveMinimumValue()` - Numeric exclusive 
bounds (replaces boolean flags)
+  - `contentMediaType()` - MIME type for string contents (e.g., 
`"application/json"`)
+  - `contentEncoding()` - Encoding for string contents (e.g., `"base64"`)
+  - `prefixItems()` - Tuple validation for array prefixes
+  - `unevaluatedItems()` - Additional validation for unevaluated array items
+  - `unevaluatedProperties()` - Additional validation for unevaluated object 
properties
+  - `dependentSchemas()` - Conditional subschemas based on property presence
+  - `dependentRequired()` - Conditionally required properties
+  - `_if()` / `_then()` / `_else()` - Conditional schema validation
+  - `$defs()` - Reusable schema definitions (replaces `definitions`)
+
+  **Example Usage**:
+  ```java
+  @Schema(
+      type="integer",
+      _const="FIXED_VALUE",
+      examples={"100", "200", "300"},
+      $comment="Internal use only",
+      deprecatedProperty=true,
+      exclusiveMaximumValue="1000",
+      exclusiveMinimumValue="0"
+  )
+  public int legacyField;
+  ```
+
+- **Backward Compatibility**: 
+  - Old boolean `exclusiveMaximum` / `exclusiveMinimum` properties still work 
but are deprecated
+  - New numeric `exclusiveMaximumValue` / `exclusiveMinimumValue` take 
precedence when both are specified
+  - All Draft 04 properties continue to function as before
+
+  **Migration Example**:
+  ```java
+  // Draft 04 style (still works, but deprecated)
+  @Schema(
+      type="integer",
+      exclusiveMaximum=true,  // deprecated
+      maximum="100",
+      exclusiveMinimum=true,  // deprecated
+      minimum="0"
+  )
+  
+  // Draft 2020-12 style (recommended)
+  @Schema(
+      type="integer",
+      exclusiveMaximumValue="100",  // 0 < x < 100 (boundaries excluded)
+      exclusiveMinimumValue="0"
+  )
+  ```
+
+- **Enhanced Validation**: 
+  - Added `const` validation in `HttpPartSchema` - values must exactly match 
the constant
+  - Updated `exclusiveMaximum` / `exclusiveMinimum` validation to support both 
boolean flags (Draft 04) and numeric values (Draft 2020-12)
+  - Proper error messages for all new validation types
+
+- **Comprehensive Test Coverage**: Added 38 new unit tests covering:
+  - All 18 new Draft 2020-12 properties
+  - Schema annotation building and processing
+  - JSON schema generation
+  - Runtime validation (input and output)
+  - Backward compatibility scenarios
+  - Precedence rules (new vs old style)
+
 #### XML Serialization
 
 - **Text Node Delimiter**: Added `textNodeDelimiter` property to 
`XmlSerializer` and `HtmlSerializer` to control spacing between consecutive 
text nodes.
@@ -388,6 +461,22 @@ None at this time.
   - `juneau-shaded-rest-client` - For REST client work (3.8 MB)
   - `juneau-shaded-rest-server` - For REST server work (3.8 MB)
 
+- **@Schema Annotation**: All existing code using `@Schema` annotations will 
continue to work without changes. The upgrade to Draft 2020-12 is fully 
backward compatible.
+  
+  **Optional Migration** (for new Draft 2020-12 features):
+  - Replace `exclusiveMaximum=true, maximum="100"` with 
`exclusiveMaximumValue="100"` for cleaner syntax
+  - Replace `exclusiveMinimum=true, minimum="0"` with 
`exclusiveMinimumValue="0"` for cleaner syntax
+  - Use new properties like `_const`, `examples`, `$comment`, etc. as needed
+  
+  **Example:**
+  ```java
+  // Old style (still works, but deprecated)
+  @Schema(type="integer", exclusiveMaximum=true, maximum="100", 
exclusiveMinimum=true, minimum="0")
+  
+  // New style (recommended)
+  @Schema(type="integer", exclusiveMaximumValue="100", 
exclusiveMinimumValue="0")
+  ```
+
 - **JsonSchema**: Code using `JsonSchema` beans will continue to work without 
changes. For Draft 2020-12 features:
   - Use `$id` instead of `id` for new schemas
   - Use `$defs` instead of `definitions` for new schemas
diff --git a/juneau-examples/juneau-examples-rest-jetty/build-overlay/pom.xml 
b/juneau-examples/juneau-examples-rest-jetty/build-overlay/pom.xml
index fd3caab21e..c497f21d54 100644
--- a/juneau-examples/juneau-examples-rest-jetty/build-overlay/pom.xml
+++ b/juneau-examples/juneau-examples-rest-jetty/build-overlay/pom.xml
@@ -60,7 +60,7 @@
                <!-- Optional RDF support -->
                <dependency>
                        <groupId>org.apache.juneau</groupId>
-                       <artifactId>juneau-marshall-rdf</artifactId>
+                       <artifactId>juneau-marshall</artifactId>
                        <version>\${juneau.version}</version>
                </dependency>
 
diff --git a/juneau-shaded/juneau-shaded-rest-server/pom.xml 
b/juneau-shaded/juneau-shaded-rest-server/pom.xml
index 40e8acf1b4..c6a0bb95fa 100644
--- a/juneau-shaded/juneau-shaded-rest-server/pom.xml
+++ b/juneau-shaded/juneau-shaded-rest-server/pom.xml
@@ -28,7 +28,7 @@
        <artifactId>juneau-shaded-rest-server</artifactId>
        <name>Apache Juneau REST Server (Shaded)</name>
        <description>
-               Shaded JAR containing juneau-core modules plus 
juneau-rest-common, juneau-rest-server, juneau-rest-server-rdf, and 
juneau-rest-mock.
+               Shaded JAR containing juneau-core modules plus 
juneau-rest-common, juneau-rest-server, and juneau-rest-mock.
                This artifact provides everything needed for REST server 
development with Juneau.
        </description>
 
@@ -51,11 +51,6 @@
                        <artifactId>juneau-rest-server</artifactId>
                        <version>${project.version}</version>
                </dependency>
-               <dependency>
-                       <groupId>org.apache.juneau</groupId>
-                       <artifactId>juneau-rest-server-rdf</artifactId>
-                       <version>${project.version}</version>
-               </dependency>
                <dependency>
                        <groupId>org.apache.juneau</groupId>
                        <artifactId>juneau-rest-mock</artifactId>
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/httppart/HttpPartSchema_Body_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/httppart/HttpPartSchema_Body_Test.java
index cab71d25df..33a40658a1 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/httppart/HttpPartSchema_Body_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/httppart/HttpPartSchema_Body_Test.java
@@ -805,4 +805,146 @@ class HttpPartSchema_Body_Test extends TestBase {
                assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum number of items exceeded.", 
()->s.getItems().getItems().getItems().validateOutput(split("1,2,3,4,5"), 
BeanContext.DEFAULT));
                assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum number of items exceeded.", 
()->s.getItems().getItems().getItems().getItems().validateOutput(split("1,2,3,4,5,6"),
 BeanContext.DEFAULT));
        }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // JSON Schema Draft 2020-12 validation tests
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Content
+       @Schema(_const="CONSTANT_VALUE")
+       public static class D01a {}
+
+       @Test void d01a_const_valid() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D01a.class).build();
+               s.validateInput("CONSTANT_VALUE");
+               s.validateInput(null);  // null is allowed when not required
+       }
+
+       @Test void d01a_const_invalid() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D01a.class).build();
+               assertThrowsWithMessage(SchemaValidationException.class, "Value 
does not match constant.  Must be: CONSTANT_VALUE", 
()->s.validateInput("OTHER_VALUE"));
+       }
+
+       @Content
+       @Schema(_const="CONSTANT_VALUE", required=true)
+       public static class D01b {}
+
+       @Test void d01b_const_required() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D01b.class).build();
+               s.validateInput("CONSTANT_VALUE");
+               assertThrowsWithMessage(SchemaValidationException.class, "No 
value specified.", ()->s.validateInput(null));
+       }
+
+       @Content
+       @Schema(t="integer", exclusiveMaximumValue="100", 
exclusiveMinimumValue="0")
+       public static class D02a {}
+
+       @Test void d02a_exclusiveNumericBounds() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D02a.class).build();
+               s.validateOutput(1, BeanContext.DEFAULT);
+               s.validateOutput(50, BeanContext.DEFAULT);
+               s.validateOutput(99, BeanContext.DEFAULT);
+               s.validateOutput(null, BeanContext.DEFAULT);
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(0, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(100, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(-1, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(101, BeanContext.DEFAULT));
+       }
+
+       @Content
+       @Schema(t="number", exclusiveMaximumValue="10.5", 
exclusiveMinimumValue="0.5")
+       public static class D02b {}
+
+       @Test void d02b_exclusiveNumericBounds_doubles() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D02b.class).build();
+               s.validateOutput(0.6, BeanContext.DEFAULT);
+               s.validateOutput(5.0, BeanContext.DEFAULT);
+               s.validateOutput(10.4, BeanContext.DEFAULT);
+               s.validateOutput(null, BeanContext.DEFAULT);
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(0.5, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(10.5, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(0.4, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(10.6, BeanContext.DEFAULT));
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Backward compatibility: Old boolean exclusiveMaximum/exclusiveMinimum
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Content
+       @Schema(t="integer", exclusiveMaximum=true, exclusiveMinimum=true, 
maximum="100", minimum="0")
+       public static class D03a {}
+
+       @Test void d03a_exclusiveBooleanBounds() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D03a.class).build();
+               s.validateOutput(1, BeanContext.DEFAULT);
+               s.validateOutput(50, BeanContext.DEFAULT);
+               s.validateOutput(99, BeanContext.DEFAULT);
+               s.validateOutput(null, BeanContext.DEFAULT);
+               // With boolean flags, 0 and 100 are excluded
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(0, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(100, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(-1, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(101, BeanContext.DEFAULT));
+       }
+
+       @Content
+       @Schema(t="integer", exclusiveMaximum=false, exclusiveMinimum=false, 
maximum="100", minimum="0")
+       public static class D03b {}
+
+       @Test void d03b_inclusiveBounds() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D03b.class).build();
+               // With boolean flags set to false, 0 and 100 are included
+               s.validateOutput(0, BeanContext.DEFAULT);
+               s.validateOutput(1, BeanContext.DEFAULT);
+               s.validateOutput(50, BeanContext.DEFAULT);
+               s.validateOutput(99, BeanContext.DEFAULT);
+               s.validateOutput(100, BeanContext.DEFAULT);
+               s.validateOutput(null, BeanContext.DEFAULT);
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(-1, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(101, BeanContext.DEFAULT));
+       }
+
+       @Content
+       @Schema(t="integer", exclusiveMaximumValue="100", 
exclusiveMinimumValue="0", exclusiveMaximum=false, exclusiveMinimum=false)
+       public static class D03c {}
+
+       @Test void d03c_newStyleTakesPrecedence() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D03c.class).build();
+               // New numeric style should take precedence over old boolean 
flags
+               s.validateOutput(1, BeanContext.DEFAULT);
+               s.validateOutput(50, BeanContext.DEFAULT);
+               s.validateOutput(99, BeanContext.DEFAULT);
+               s.validateOutput(null, BeanContext.DEFAULT);
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Minimum value not met.", ()->s.validateOutput(0, BeanContext.DEFAULT));
+               assertThrowsWithMessage(SchemaValidationException.class, 
"Maximum value exceeded.", ()->s.validateOutput(100, BeanContext.DEFAULT));
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Deprecated property (no validation, just ensures it's settable)
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Content
+       @Schema(deprecatedProperty=true)
+       public static class D04a {}
+
+       @Test void d04a_deprecated() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D04a.class).build();
+               // deprecated is just a flag, doesn't affect validation
+               assertBean(s, "deprecated", "true");
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Examples (no validation, documentation only)
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @Content
+       @Schema(examples={"example1", "example2", "example3"})
+       public static class D05a {}
+
+       @Test void d05a_examples() throws Exception {
+               var s = HttpPartSchema.create().applyAll(Content.class, 
D05a.class).build();
+               // examples are documentation only, doesn't affect validation
+               assertBean(s, "examples", "[example1,example2,example3]");
+       }
 }
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorTest.java
 
b/juneau-utest/src/test/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorTest.java
index 2b87065530..828c9f67ba 100755
--- 
a/juneau-utest/src/test/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorTest.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/jsonschema/JsonSchemaGeneratorTest.java
@@ -1307,4 +1307,232 @@ class JsonSchemaGeneratorTest extends TestBase {
                assertContains("$ref", r(x.getSchema(new B())));
        }
 
+       
//====================================================================================================
+       // JSON Schema Draft 2020-12 properties
+       
//====================================================================================================
+
+       @Test void draft2020_const() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(ConstBean.class);
+               assertBean(schema, "const", "MY_CONSTANT");
+       }
+
+       @Schema(type="string", _const="MY_CONSTANT")
+       public static class ConstBean {
+               public String value;
+       }
+
+       @Test void draft2020_examples() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(ExamplesBean.class);
+               assertBean(schema, "examples", "[example1,example2,example3]");
+       }
+
+       @Schema(type="string", examples={"example1", "example2", "example3"})
+       public static class ExamplesBean {
+               public String value;
+       }
+
+       @Test void draft2020_deprecated() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(DeprecatedBean.class);
+               assertBean(schema, "deprecated", "true");
+       }
+
+       @Schema(type="string", deprecatedProperty=true)
+       public static class DeprecatedBean {
+               public String value;
+       }
+
+       @Test void draft2020_comment() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(CommentBean.class);
+               assertBean(schema, "$comment", "This is a comment for 
developers");
+       }
+
+       @Schema(type="string", $comment="This is a comment for developers")
+       public static class CommentBean {
+               public String value;
+       }
+
+       @Test void draft2020_exclusiveMaxMinNumeric() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(ExclusiveNumericBean.class);
+               assertBean(schema, "exclusiveMaximum,exclusiveMinimum", 
"100,0");
+       }
+
+       @Schema(type="integer", exclusiveMaximumValue="100", 
exclusiveMinimumValue="0")
+       public static class ExclusiveNumericBean {
+               public int value;
+       }
+
+       @Test void draft2020_contentMediaType() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(ContentMediaTypeBean.class);
+               assertBean(schema, "contentMediaType", "application/json");
+       }
+
+       @Schema(type="string", contentMediaType="application/json")
+       public static class ContentMediaTypeBean {
+               public String jsonData;
+       }
+
+       @Test void draft2020_contentEncoding() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(ContentEncodingBean.class);
+               assertBean(schema, "contentEncoding", "base64");
+       }
+
+       @Schema(type="string", contentEncoding="base64")
+       public static class ContentEncodingBean {
+               public String encodedData;
+       }
+
+       @Test void draft2020_$id() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(IdBean.class);
+               assertBean(schema, "$id", 
"https://example.com/schemas/my-schema";);
+       }
+
+       @Schema(type="object", $id="https://example.com/schemas/my-schema";)
+       public static class IdBean {
+               public String value;
+       }
+
+       @Test void draft2020_$defs() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(DefsBean.class);
+               assertBean(schema, "$defs", "MyDef:{type:'string'}");
+       }
+
+       @Schema(type="object", $defs="MyDef:{type:'string'}")
+       public static class DefsBean {
+               public String value;
+       }
+
+       @Test void draft2020_prefixItems() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(PrefixItemsBean.class);
+               // prefixItems is stored as newline-separated string, just 
verify it exists
+               assertContains("string", schema.get("prefixItems").toString());
+       }
+
+       @Schema(type="array", prefixItems={"string", "number"})
+       public static class PrefixItemsBean {
+               public Object[] tuple;
+       }
+
+       @Test void draft2020_unevaluatedItems() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(UnevaluatedItemsBean.class);
+               assertBean(schema, "unevaluatedItems", "false");
+       }
+
+       @Schema(type="array", unevaluatedItems="false")
+       public static class UnevaluatedItemsBean {
+               public Object[] items;
+       }
+
+       @Test void draft2020_unevaluatedProperties() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(UnevaluatedPropertiesBean.class);
+               assertBean(schema, "unevaluatedProperties", "false");
+       }
+
+       @Schema(type="object", unevaluatedProperties="false")
+       public static class UnevaluatedPropertiesBean {
+               public String prop1;
+       }
+
+       @Test void draft2020_dependentSchemas() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(DependentSchemasBean.class);
+               assertBean(schema, "dependentSchemas", "prop1:{type:'string'}");
+       }
+
+       @Schema(type="object", dependentSchemas="prop1:{type:'string'}")
+       public static class DependentSchemasBean {
+               public String prop1;
+               public String prop2;
+       }
+
+       @Test void draft2020_dependentRequired() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(DependentRequiredBean.class);
+               assertBean(schema, "dependentRequired", "prop1:prop2,prop3");
+       }
+
+       @Schema(type="object", dependentRequired="prop1:prop2,prop3")
+       public static class DependentRequiredBean {
+               public String prop1;
+               public String prop2;
+               public String prop3;
+       }
+
+       @Test void draft2020_conditionals() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(ConditionalBean.class);
+               assertBean(schema, "if,then,else", 
"properties:{foo:{const:'bar'}},required:['baz'],required:['qux']");
+       }
+
+       @Schema(type="object", 
+               _if="properties:{foo:{const:'bar'}}", 
+               _then="required:['baz']", 
+               _else="required:['qux']")
+       public static class ConditionalBean {
+               public String foo;
+               public String baz;
+               public String qux;
+       }
+
+       @Test void draft2020_combinedProperties() throws Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(CombinedPropertiesBean.class);
+               assertBean(schema, 
"$comment,$id,const,deprecated,examples,exclusiveMaximum,exclusiveMinimum", 
+                       "A comprehensive 
example,https://example.com/combined,FIXED,true,[ex1,ex2],100,0";);
+       }
+
+       @Schema(type="integer",
+               $id="https://example.com/combined";,
+               _const="FIXED",
+               examples={"ex1", "ex2"},
+               $comment="A comprehensive example",
+               deprecatedProperty=true,
+               exclusiveMaximumValue="100",
+               exclusiveMinimumValue="0")
+       public static class CombinedPropertiesBean {
+               public int value;
+       }
+
+       
//====================================================================================================
+       // Backward compatibility: Old boolean exclusiveMaximum/exclusiveMinimum
+       
//====================================================================================================
+
+       @Test void backwardCompatibility_exclusiveMaxMin_boolean() throws 
Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               var schema = s.getSchema(OldStyleExclusiveBean.class);
+               assertBean(schema, 
"exclusiveMaximum,exclusiveMinimum,maximum,minimum", "true,true,100,0");
+       }
+
+       @Schema(type="integer", exclusiveMaximum=true, exclusiveMinimum=true, 
maximum="100", minimum="0")
+       public static class OldStyleExclusiveBean {
+               public int value;
+       }
+
+       @Test void backwardCompatibility_newStyleTakesPrecedence() throws 
Exception {
+               var s = JsonSchemaGenerator.DEFAULT.getSession();
+               // New numeric style should take precedence in asMap() when 
both are set
+               var schema = s.getSchema(MixedStyleExclusiveBean.class);
+               assertBean(schema, "exclusiveMaximum,exclusiveMinimum", 
"100,0");
+       }
+
+       @Schema(type="integer", 
+               exclusiveMaximumValue="100", 
+               exclusiveMinimumValue="0",
+               exclusiveMaximum=false,  // This should be ignored in favor of 
numeric values
+               exclusiveMinimum=false)
+       public static class MixedStyleExclusiveBean {
+               public int value;
+       }
+
 }
\ No newline at end of file
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaAnnotation_Test.java
index 76c1db7744..8fee11265b 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/jsonschema/annotation/SchemaAnnotation_Test.java
@@ -321,4 +321,154 @@ class SchemaAnnotation_Test extends TestBase {
                assertNotEqualsAny(a1.hashCode(), 0, -1);
                assertEqualsAll(a1.hashCode(), d1.hashCode(), d2.hashCode());
        }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // JSON Schema Draft 2020-12 properties
+       
//------------------------------------------------------------------------------------------------------------------
+
+       Schema draft2020_1 = SchemaAnnotation.create()
+               ._const("constantValue")
+               .examples("example1", "example2")
+               .$comment("This is a schema comment")
+               .deprecatedProperty(true)
+               .exclusiveMaximumValue("100")
+               .exclusiveMinimumValue("0")
+               .contentMediaType("application/json")
+               .contentEncoding("base64")
+               .prefixItems("string", "number")
+               .unevaluatedItems("false")
+               .unevaluatedProperties("false")
+               .dependentSchemas("prop1:{type:'string'}")
+               .dependentRequired("prop1:prop2,prop3")
+               ._if("properties:{foo:{const:'bar'}}")
+               ._then("required:['baz']")
+               ._else("required:['qux']")
+               .$defs("MyDef:{type:'string'}")
+               .$id("https://example.com/schemas/my-schema";)
+               .build();
+
+       Schema draft2020_2 = SchemaAnnotation.create()
+               ._const("constantValue")
+               .examples("example1", "example2")
+               .$comment("This is a schema comment")
+               .deprecatedProperty(true)
+               .exclusiveMaximumValue("100")
+               .exclusiveMinimumValue("0")
+               .contentMediaType("application/json")
+               .contentEncoding("base64")
+               .prefixItems("string", "number")
+               .unevaluatedItems("false")
+               .unevaluatedProperties("false")
+               .dependentSchemas("prop1:{type:'string'}")
+               .dependentRequired("prop1:prop2,prop3")
+               ._if("properties:{foo:{const:'bar'}}")
+               ._then("required:['baz']")
+               ._else("required:['qux']")
+               .$defs("MyDef:{type:'string'}")
+               .$id("https://example.com/schemas/my-schema";)
+               .build();
+
+       @Test void e01_draft2020_basic() {
+               assertBean(draft2020_1,
+                       
"$comment,$defs,$id,_const,_else,_if,_then,contentEncoding,contentMediaType,dependentRequired,dependentSchemas,deprecatedProperty,examples,exclusiveMaximumValue,exclusiveMinimumValue,prefixItems,unevaluatedItems,unevaluatedProperties",
+                       "[This is a schema 
comment],[MyDef:{type:'string'}],https://example.com/schemas/my-schema,[constantValue],[required:['qux']],[properties:{foo:{const:'bar'}}],[required:['baz']],base64,application/json,[prop1:prop2,prop3],[prop1:{type:'string'}],true,[example1,example2],100,0,[string,number],[false],[false]");
+       }
+
+       @Test void e02_draft2020_testEquivalency() {
+               assertEquals(draft2020_2, draft2020_1);
+               assertNotEqualsAny(draft2020_1.hashCode(), 0, -1);
+               assertEquals(draft2020_1.hashCode(), draft2020_2.hashCode());
+       }
+
+       @Test void e03_draft2020_testEquivalencyInPropertyStores() {
+               var bc1 = BeanContext.create().annotations(draft2020_1).build();
+               var bc2 = BeanContext.create().annotations(draft2020_2).build();
+               assertSame(bc1, bc2);
+       }
+
+       @Schema(
+               _const="constantValue",
+               examples={"example1", "example2"},
+               $comment="This is a schema comment",
+               deprecatedProperty=true,
+               exclusiveMaximumValue="100",
+               exclusiveMinimumValue="0",
+               contentMediaType="application/json",
+               contentEncoding="base64",
+               prefixItems={"string", "number"},
+               unevaluatedItems="false",
+               unevaluatedProperties="false",
+               dependentSchemas="prop1:{type:'string'}",
+               dependentRequired="prop1:prop2,prop3",
+               _if="properties:{foo:{const:'bar'}}",
+               _then="required:['baz']",
+               _else="required:['qux']",
+               $defs="MyDef:{type:'string'}",
+               $id="https://example.com/schemas/my-schema";
+       )
+       public static class E1 {}
+       Schema e1 = E1.class.getAnnotationsByType(Schema.class)[0];
+
+       @Schema(
+               _const="constantValue",
+               examples={"example1", "example2"},
+               $comment="This is a schema comment",
+               deprecatedProperty=true,
+               exclusiveMaximumValue="100",
+               exclusiveMinimumValue="0",
+               contentMediaType="application/json",
+               contentEncoding="base64",
+               prefixItems={"string", "number"},
+               unevaluatedItems="false",
+               unevaluatedProperties="false",
+               dependentSchemas="prop1:{type:'string'}",
+               dependentRequired="prop1:prop2,prop3",
+               _if="properties:{foo:{const:'bar'}}",
+               _then="required:['baz']",
+               _else="required:['qux']",
+               $defs="MyDef:{type:'string'}",
+               $id="https://example.com/schemas/my-schema";
+       )
+       public static class E2 {}
+       Schema e2 = E2.class.getAnnotationsByType(Schema.class)[0];
+
+       @Test void e04_draft2020_comparisonWithDeclarativeAnnotations() {
+               assertEqualsAll(draft2020_1, e1, e2);
+               assertNotEqualsAny(draft2020_1.hashCode(), 0, -1);
+               assertEqualsAll(draft2020_1.hashCode(), e1.hashCode(), 
e2.hashCode());
+       }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Backward compatibility: exclusiveMaximum/exclusiveMinimum fallback
+       
//------------------------------------------------------------------------------------------------------------------
+
+       @Test void f01_backwardCompatibility_exclusiveMaxMin() {
+               // Test that old boolean exclusiveMaximum/exclusiveMinimum 
still work
+               Schema oldStyle = SchemaAnnotation.create()
+                       .exclusiveMaximum(true)
+                       .exclusiveMinimum(true)
+                       .maximum("100")
+                       .minimum("0")
+                       .build();
+               
+               assertBean(oldStyle, 
"exclusiveMaximum,exclusiveMinimum,maximum,minimum", "true,true,100,0");
+               
+               // Test that new numeric style takes precedence
+               Schema newStyle = SchemaAnnotation.create()
+                       .exclusiveMaximumValue("100")
+                       .exclusiveMinimumValue("0")
+                       .build();
+               
+               assertBean(newStyle, 
"exclusiveMaximumValue,exclusiveMinimumValue", "100,0");
+               
+               // Test that new style takes precedence when both are set
+               Schema mixed = SchemaAnnotation.create()
+                       .exclusiveMaximum(false)
+                       .exclusiveMinimum(false)
+                       .exclusiveMaximumValue("100")
+                       .exclusiveMinimumValue("0")
+                       .build();
+               
+               assertBean(mixed, 
"exclusiveMaximum,exclusiveMinimum,exclusiveMaximumValue,exclusiveMinimumValue",
 "false,false,100,0");
+       }
 }
\ No newline at end of file

Reply via email to