Repository: eclipse/microprofile-open-api
Pull request #598 (2024-02-06 17:53:54)

OAS 3.1 Schema model updates
For #584 

Azquelt: I've pushed commits to address the comments above and to add tests for reading and passing through a custom schema dialect via a model reader and a static document.

I think these are all the tests needed for the model parts of #584, so if these are approved I will squash the review change commits and merge this PR.

Patch:
diff --git a/api/pom.xml b/api/pom.xml
index 466875d..30e82d0 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -20,7 +20,7 @@
         <!-- This is just for now and will not work if the API has a separate release cycle than the rest. -->
         <groupId>org.eclipse.microprofile.openapi</groupId>
         <artifactId>microprofile-openapi-parent</artifactId>
-        <version>3.2-SNAPSHOT</version>
+        <version>4.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>microprofile-openapi-api</artifactId>
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Schema.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Schema.java
index 7af9437..6a7e45b 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Schema.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/media/Schema.java
@@ -21,6 +21,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 
+import org.eclipse.microprofile.openapi.OASFactory;
 import org.eclipse.microprofile.openapi.models.Constructible;
 import org.eclipse.microprofile.openapi.models.Extensible;
 import org.eclipse.microprofile.openapi.models.ExternalDocumentation;
@@ -47,7 +48,8 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * The values allowed for the in field.
      */
     enum SchemaType {
-        INTEGER("integer"), NUMBER("number"), BOOLEAN("boolean"), STRING("string"), OBJECT("object"), ARRAY("array");
+        INTEGER("integer"), NUMBER("number"), BOOLEAN("boolean"), STRING("string"), OBJECT("object"), ARRAY(
+                "array"), NULL("null");
 
         private final String value;
 
@@ -212,7 +214,7 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     /**
      * Returns the maximum property from this Schema instance.
      *
-     * @return the maximum value of a numeric object
+     * @return the value that a numeric object must be less than or equal to
      **/
     BigDecimal getMaximum();
 
@@ -220,7 +222,7 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * Sets the maximum property of this Schema instance to the value given.
      *
      * @param maximum
-     *            specifies the maximum numeric value of objects defined by this Schema
+     *            the value that a numeric object must be less than or equal to
      */
     void setMaximum(BigDecimal maximum);
 
@@ -228,7 +230,7 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * Sets the maximum property of this Schema instance to the value given.
      *
      * @param maximum
-     *            specifies the maximum numeric value of objects defined by this Schema
+     *            the value that a numeric object must be less than or equal to
      * @return the current Schema instance
      */
     default Schema maximum(BigDecimal maximum) {
@@ -239,28 +241,29 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     /**
      * Returns the exclusiveMaximum property from this Schema instance.
      *
-     * @return whether the numeric value of objects must be less than the maximum property
-     **/
-    Boolean getExclusiveMaximum();
+     * @return the value that a numeric object must be less than
+     * @since 4.0
+     */
+    BigDecimal getExclusiveMaximum();
 
     /**
      * Sets the exclusiveMaximum property of this Schema instance to the value given.
      *
      * @param exclusiveMaximum
-     *            when true the numeric value of objects defined by this Schema must be less than indicated by the
-     *            maximum property
+     *            the value that a numeric object must be less than
+     * @since 4.0
      */
-    void setExclusiveMaximum(Boolean exclusiveMaximum);
+    void setExclusiveMaximum(BigDecimal exclusiveMaximum);
 
     /**
      * Sets the exclusiveMaximum property of this Schema instance to the value given.
      *
      * @param exclusiveMaximum
-     *            when true the numeric value of objects defined by this Schema must be less than indicated by the
-     *            maximum property
+     *            the value that a numeric object must be less than
      * @return the current Schema instance
+     * @since 4.0
      */
-    default Schema exclusiveMaximum(Boolean exclusiveMaximum) {
+    default Schema exclusiveMaximum(BigDecimal exclusiveMaximum) {
         setExclusiveMaximum(exclusiveMaximum);
         return this;
     }
@@ -268,7 +271,7 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     /**
      * Returns the minimum property from this Schema instance.
      *
-     * @return the minimum value of a numeric object
+     * @return the value that a numeric object must be greater than or equal to
      **/
     BigDecimal getMinimum();
 
@@ -276,7 +279,7 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * Sets the minimum property of this Schema instance to the value given.
      *
      * @param minimum
-     *            specifies the minimum numeric value of objects defined by this Schema
+     *            the value that a numeric object must be greater than or equal to
      */
     void setMinimum(BigDecimal minimum);
 
@@ -284,7 +287,7 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * Sets the minimum property of this Schema instance to the value given.
      *
      * @param minimum
-     *            specifies the minimum numeric value of objects defined by this Schema
+     *            the value that a numeric object must be greater than or equal to
      * @return the current Schema instance
      */
     default Schema minimum(BigDecimal minimum) {
@@ -295,28 +298,29 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     /**
      * Returns the exclusiveMinimum property from this Schema instance.
      *
-     * @return whether the numeric value of objects must be greater than the minimum property
-     **/
-    Boolean getExclusiveMinimum();
+     * @return the value that a numeric object must be greater than
+     * @since 4.0
+     */
+    BigDecimal getExclusiveMinimum();
 
     /**
      * Sets the exclusiveMinimum property of this Schema instance to the value given.
      *
      * @param exclusiveMinimum
-     *            when true the numeric value of objects defined by this Schema must be greater than indicated by the
-     *            minimum property
+     *            the value that a numeric object must be greater than
+     * @since 4.0
      */
-    void setExclusiveMinimum(Boolean exclusiveMinimum);
+    void setExclusiveMinimum(BigDecimal exclusiveMinimum);
 
     /**
      * Sets the exclusiveMinimum property of this Schema instance to the value given.
      *
      * @param exclusiveMinimum
-     *            when true the numeric value of objects defined by this Schema must be greater than indicated by the
-     *            minimum property
+     *            the value that a numeric object must be greater than
      * @return the current Schema instance
+     * @since 4.0
      */
-    default Schema exclusiveMinimum(Boolean exclusiveMinimum) {
+    default Schema exclusiveMinimum(BigDecimal exclusiveMinimum) {
         setExclusiveMinimum(exclusiveMinimum);
         return this;
     }
@@ -594,32 +598,52 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     void removeRequired(String required);
 
     /**
-     * Returns the type property from this Schema.
+     * Returns the type property of this Schema instance. Defines the types which are valid.
      *
-     * @return the type used in this Schema. Default value <b>must</b> be <code>null</code>
-     **/
-    SchemaType getType();
+     * @return a copy List (potentially immutable) of the allowed types
+     */
+    List<SchemaType> getType();
 
     /**
-     * Sets the type used by this Schema to the string given.
+     * Sets the type property of this Schema instance. Defines the types which are valid.
      *
-     * @param type
-     *            the type used by this Schema or <code>null</code> for reference schemas
+     * @param types
+     *            a list of the allowed types
      */
-    void setType(SchemaType type);
+    void setType(List<SchemaType> types);
 
     /**
-     * Sets the type used by this Schema to the string given.
+     * Sets the type property of this Schema instance. Defines the types which are valid.
      *
-     * @param type
-     *            the type used by this Schema or <code>null</code> for reference schemas
-     * @return the current Schema instance
+     * @param types
+     *            a list of the allowed types
+     * @return current Schema instance
+     * @since "4.0"
      */
-    default Schema type(SchemaType type) {
-        setType(type);
+    default Schema type(List<SchemaType> types) {
+        setType(types);
         return this;
     }
 
+    /**
+     * Adds a type to the type list.
+     *
+     * @param type
+     *            the type to add to the type list
+     * @return current Schema instance
+     * @since "4.0"
+     */
+    Schema addType(SchemaType type);
+
+    /**
+     * Removes a type from the type list.
+     *
+     * @param type
+     *            the type to remove from the type list
+     * @since "4.0"
+     */
+    void removeType(SchemaType type);
+
     /**
      * Returns a Schema which describes properties not allowed in objects defined by the current schema.
      *
@@ -727,7 +751,9 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * </ul>
      *
      * @return this Schema's additionalProperties property (as {@link Boolean})
+     * @deprecated use {@link #getAdditionalPropertiesSchema()} which may return a boolean-valued schema
      */
+    @Deprecated(since = "4.0")
     Boolean getAdditionalPropertiesBoolean();
 
     /**
@@ -749,7 +775,9 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      *
      * @param additionalProperties
      *            a Schema which defines additional properties
+     * @deprecated use {@link #setAdditionalPropertiesSchema(Schema)} with a boolean-valued schema
      */
+    @Deprecated(since = "4.0")
     void setAdditionalPropertiesBoolean(Boolean additionalProperties);
 
     /**
@@ -775,7 +803,9 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * @param additionalProperties
      *            a Schema which defines additional properties
      * @return the current Schema instance
+     * @deprecated use {@link #additionalPropertiesSchema(Schema)} with a boolean-valued schema
      */
+    @Deprecated(since = "4.0")
     default Schema additionalPropertiesBoolean(Boolean additionalProperties) {
         setAdditionalPropertiesBoolean(additionalProperties);
         return this;
@@ -838,33 +868,6 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
         return this;
     }
 
-    /**
-     * Returns the nullable property from this Schema instance which indicates whether null is a valid value.
-     *
-     * @return the nullable property
-     **/
-    Boolean getNullable();
-
-    /**
-     * Sets the nullable property of this Schema instance. Specify true if this Schema will allow null values.
-     *
-     * @param nullable
-     *            a boolean value indicating this Schema allows a null value.
-     */
-    void setNullable(Boolean nullable);
-
-    /**
-     * Sets the nullable property of this Schema instance. Specify true if this Schema will allow null values.
-     *
-     * @param nullable
-     *            a boolean value indicating this Schema allows a null value.
-     * @return the current Schema instance
-     */
-    default Schema nullable(Boolean nullable) {
-        setNullable(nullable);
-        return this;
-    }
-
     /**
      * Returns the readOnly property from this Schema instance.
      *
@@ -873,18 +876,18 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     Boolean getReadOnly();
 
     /**
-     * Sets the readOnly property of this Schema. Only valid when the Schema is the property in an object.
+     * Sets the readOnly property of this Schema.
      *
      * @param readOnly
-     *            true indicates the Schema should not be sent as part of a request message
+     *            {@code true} indicates the Schema should not be sent as part of a request message
      */
     void setReadOnly(Boolean readOnly);
 
     /**
-     * Sets the readOnly property of this Schema. Only valid when the Schema is the property in an object.
+     * Sets the readOnly property of this Schema.
      *
      * @param readOnly
-     *            true indicates the Schema should not be sent as part of a request message
+     *            {@code true} indicates the Schema should not be sent as part of a request message
      * @return the current Schema instance
      */
     default Schema readOnly(Boolean readOnly) {
@@ -900,18 +903,18 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
     Boolean getWriteOnly();
 
     /**
-     * Sets the writeOnly property of this Schema. Only valid when the Schema is the property in an object.
+     * Sets the writeOnly property of this Schema.
      *
      * @param writeOnly
-     *            true indicates the Schema should not be sent as part of a response message
+     *            {@code true} indicates the Schema should not be sent as part of a response message
      */
     void setWriteOnly(Boolean writeOnly);
 
     /**
-     * Sets the writeOnly property of this Schema. Only valid when the Schema is the property in an object.
+     * Sets the writeOnly property of this Schema.
      *
      * @param writeOnly
-     *            true indicates the Schema should not be sent as part of a response message
+     *            {@code true} indicates the Schema should not be sent as part of a response message
      * @return the current Schema instance
      */
     default Schema writeOnly(Boolean writeOnly) {
@@ -923,7 +926,9 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * Returns the example property from this Schema instance.
      *
      * @return an object which is an example of an instance of this Schema
-     **/
+     * @deprecated use {@link #getExamples()}
+     */
+    @Deprecated(since = "4.0")
     Object getExample();
 
     /**
@@ -932,7 +937,9 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      *
      * @param example
      *            an object which is an instance of this Schema
+     * @deprecated use {@link #setExamples(List)}
      */
+    @Deprecated(since = "4.0")
     void setExample(Object example);
 
     /**
@@ -942,7 +949,9 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      * @param example
      *            an object which is an instance of this Schema
      * @return the current Schema instance
+     * @deprecated use {@link #examples(List)}
      */
+    @Deprecated(since = "4.0")
     default Schema example(Object example) {
         setExample(example);
         return this;
@@ -1192,4 +1201,930 @@ public interface Schema extends Extensible<Schema>, Constructible, Reference<Sch
      */
     void removeOneOf(Schema oneOf);
 
+    /**
+     * Returns the schema dialect in use. This is the value of the {@code $schema} property.
+     *
+     * @return the schema dialect name, or {@code null} for the default
+     * @since 4.0
+     */
+    String getSchemaDialect();
+
+    /**
+     * Sets the schema dialect in use. This is the value of the {@code $schema} property.
+     *
+     * @param schemaDialect
+     *            the schema dialect name, or {@code null} for the default
+     * @since 4.0
+     */
+    void setSchemaDialect(String schemaDialect);
+
+    /**
+     * Sets the schema dialect in use. This is the value of the {@code $schema} property.
+     *
+     * @param schemaDialect
+     *            the schema dialect name, or {@code null} for the default
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema schemaDialect(String schemaDialect) {
+        setSchemaDialect(schemaDialect);
+        return this;
+    }
+
+    /**
+     * Returns the comment to be included in the {@code $comment} property of the schema.
+     *
+     * @return the comment, or {@code null} if no comment is set
+     * @since 4.0
+     */
+    String getComment();
+
+    /**
+     * Sets the comment to be included in the {@code $comment} property of the schema.
+     *
+     * @param comment
+     *            the comment, or {@code null} to remove any comment
+     * @since 4.0
+     */
+    void setComment(String comment);
+
+    /**
+     * Sets the comment to be included in the {@code $comment} property of the schema.
+     *
+     * @param comment
+     *            the comment, or {@code null} to remove any comment
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema comment(String comment) {
+        setComment(comment);
+        return this;
+    }
+
+    /**
+     * Returns the "if" schema. If an object is valid against the "if" schema, then it must also be valid against the
+     * "then" schema, otherwise it must be valid against the "else" schema.
+     *
+     * @return the if schema
+     * @since 4.0
+     */
+    Schema getIfSchema();
+
+    /**
+     * Sets the "if" schema. If an object is valid against the "if" schema, then it must also be valid against the
+     * "then" schema, otherwise it must be valid against the "else" schema.
+     *
+     * @param ifSchema
+     *            the if schema
+     * @since 4.0
+     */
+    void setIfSchema(Schema ifSchema);
+
+    /**
+     * Sets the "if" schema. If an object is valid against the "if" schema, then it must also be valid against the
+     * "then" schema, otherwise it must be valid against the "else" schema.
+     *
+     * @param ifSchema
+     *            the if schema
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema ifSchema(Schema ifSchema) {
+        setIfSchema(ifSchema);
+        return this;
+    }
+
+    /**
+     * Returns the "then" schema. If an object is valid against the "if" schema, then it must also be valid against the
+     * "then" schema.
+     *
+     * @return the then schema
+     * @since 4.0
+     */
+    Schema getThenSchema();
+
+    /**
+     * Sets the "then" schema. If an object is valid against the "if" schema, then it must also be valid against the
+     * "then" schema.
+     *
+     * @param thenSchema
+     *            the then schema
+     * @since 4.0
+     */
+    void setThenSchema(Schema thenSchema);
+
+    /**
+     * Sets the "then" schema. If an object is valid against the "if" schema, then it must also be valid against the
+     * "then" schema.
+     *
+     * @param thenSchema
+     *            the then schema
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema thenSchema(Schema thenSchema) {
+        setThenSchema(thenSchema);
+        return this;
+    }
+
+    /**
+     * Returns the "else" schema. If an object is not valid against the "if" schema, then it must be valid against the
+     * "else" schema.
+     *
+     * @return the else schema
+     * @since 4.0
+     */
+    Schema getElseSchema();
+
+    /**
+     * Sets the "else" schema. If an object is not valid against the "if" schema, then it must be valid against the
+     * "else" schema.
+     *
+     * @param elseSchema
+     *            the else schema
+     * @since 4.0
+     */
+    void setElseSchema(Schema elseSchema);
+
+    /**
+     * Sets the "else" schema. If an object is not valid against the "if" schema, then it must be valid against the
+     * "else" schema.
+     *
+     * @param elseSchema
+     *            the else schema
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema elseSchema(Schema elseSchema) {
+        setElseSchema(elseSchema);
+        return this;
+    }
+
+    /**
+     * Returns the dependentSchemas property of this Schema instance.
+     * <p>
+     * For each name and property pair in the map, if the object contains a property with the given name, it must match
+     * the corresponding schema.
+     *
+     * @return a copy Map (potentially immutable) of properties and their dependent schemas
+     * @since 4.0
+     */
+    Map<String, Schema> getDependentSchemas();
+
+    /**
+     * Sets the dependentSchemas property of this Schema instance.
+     * <p>
+     * For each name and property pair in the map, if the object contains a property with the given name, it must match
+     * the corresponding schema.
+     *
+     * @param dependentSchemas
+     *            a map of properties and their dependent schemas
+     * @since 4.0
+     */
+    void setDependentSchemas(Map<String, Schema> dependentSchemas);
+
+    /**
+     * Sets the dependentSchemas property of this Schema instance.
+     * <p>
+     * For each name and property pair in the map, if the object contains a property with the given name, it must match
+     * the corresponding schema.
+     *
+     * @param dependentSchemas
+     *            a map of properties and their dependent schemas
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema dependentSchemas(Map<String, Schema> dependentSchemas) {
+        setDependentSchemas(dependentSchemas);
+        return this;
+    }
+
+    /**
+     * Sets the dependent schema for a property name.
+     * <p>
+     * If the object contains a property with the given name, it must match the dependent schema.
+     *
+     * @param propertyName
+     *            the property name
+     * @param schema
+     *            the dependent schema
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    Schema addDependentSchema(String propertyName, Schema schema);
+
+    /**
+     * Removes the dependent schema for a property name.
+     *
+     * @param propertyName
+     *            the property name
+     * @since 4.0
+     */
+    void removeDependentSchema(String propertyName);
+
+    /**
+     * Returns the prefixItems property of this Schema instance.
+     * <p>
+     * If the object is an array, the nth item in the array must match the nth schema in this list.
+     *
+     * @return a copy List (potentially immutable) of prefix item schemas
+     * @since 4.0
+     */
+    List<Schema> getPrefixItems();
+
+    /**
+     * Sets the prefixItems property of this Schema instance.
+     * <p>
+     * If the object is an array, the nth item in the array must match the nth schema in this list.
+     *
+     * @param prefixItems
+     *            a list of prefix item schemas
+     * @since 4.0
+     */
+    void setPrefixItems(List<Schema> prefixItems);
+
+    /**
+     * Sets the prefixItems property of this Schema instance.
+     * <p>
+     * If the object is an array, the nth item in the array must match the nth schema in this list.
+     *
+     * @param prefixItems
+     *            a list of prefix item schemas
+     * @return current Schema instance
+     * @since 4.0
+     */
+    default Schema prefixItems(List<Schema> prefixItems) {
+        setPrefixItems(prefixItems);
+        return this;
+    }
+
+    /**
+     * Adds a schema to the end of the prefixItems list.
+     *
+     * @param prefixItem
+     *            the schema to add to the prefixItems list
+     * @return current Schema instance
+     * @since 4.0
+     */
+    Schema addPrefixItem(Schema prefixItem);
+
+    /**
+     * Removes a schema from the prefixItems list.
+     *
+     * @param prefixItem
+     *            the schema to remove from the prefixItems list
+     * @since 4.0
+     */
+    void removePrefixItem(Schema prefixItem);
+
+    /**
+     * Returns the contains property of this Schema instance.
+     * <p>
+     * If the object is an array, at least one item in the array must match the returned schema.
+     *
+     * @return a schema that one item in the array should match
+     * @since 4.0
+     */
+    Schema getContains();
+
+    /**
+     * Sets the contains property of this Schema instance.
+     * <p>
+     * If the object is an array, at least one item in the array must match the returned schema.
+     *
+     * @param contains
+     *            a schema that one item in the array should match
+     * @since 4.0
+     */
+    void setContains(Schema contains);
+
+    /**
+     * Sets the contains property of this Schema instance.
+     * <p>
+     * If the object is an array, at least one item in the array must match the returned schema.
+     *
+     * @param contains
+     *            a schema that one item in the array should match
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema contains(Schema contains) {
+        setContains(contains);
+        return this;
+    }
+
+    /**
+     * Returns the patternProperties property from this Schema instance.
+     * <p>
+     * The value of patternProperties is a map from a regular expression to a schema. For each string and schema pair in
+     * the map, if a property name is matched by the regular expression then the value of that property must validate
+     * against the schema.
+     *
+     * @return a copy Map (potentially immutable) of regular expression and schema pairs
+     * @since 4.0
+     */
+    Map<String, Schema> getPatternProperties();
+
+    /**
+     * Sets the patternProperties property from this Schema instance.
+     * <p>
+     * The value of patternProperties is a map from a regular expression to a schema. For each string and schema pair in
+     * the map, if a property name is matched by the regular expression then the value of that property must validate
+     * against the schema.
+     *
+     * @param patternProperties
+     *            a map of regular expression and schema pairs
+     * @since 4.0
+     */
+    void setPatternProperties(Map<String, Schema> patternProperties);
+
+    /**
+     * Sets the patternProperties property from this Schema instance.
+     * <p>
+     * The value of patternProperties is a map from a regular expression to a schema. For each string and schema pair in
+     * the map, if a property name is matched by the regular expression then the value of that property must validate
+     * against the schema.
+     *
+     * @param patternProperties
+     *            a map of regular expression and schema pairs
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema patternProperties(Map<String, Schema> patternProperties) {
+        setPatternProperties(patternProperties);
+        return this;
+    }
+
+    /**
+     * Adds a regular expression and schema pair to the list of pattern properties.
+     * <p>
+     * The value of patternProperties is a map from a regular expression to a schema. For each string and schema pair in
+     * the map, if a property name is matched by the regular expression then the value of that property must validate
+     * against the schema.
+     *
+     * @param regularExpression
+     *            the regular expression to add
+     * @param schema
+     *            the schema that a property value must validate against if its name matches {@code regularExpression}
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    Schema addPatternProperty(String regularExpression, Schema schema);
+
+    /**
+     * Removes a regular expression and its corresponding schema pair from the list of pattern properties.
+     *
+     * @param regularExpression
+     *            the regular expression to remove
+     * @since 4.0
+     */
+    void removePatternProperty(String regularExpression);
+
+    /**
+     * Returns the propertyNames property from this Schema instance. Each property <em>name</em> in the object must
+     * validate against this schema.
+     *
+     * @return the schema which each property name must validate against
+     * @since 4.0
+     */
+    Schema getPropertyNames();
+
+    /**
+     * Sets the propertyNames property from this Schema instance. Each property <em>name</em> in the object must
+     * validate against this schema.
+     *
+     * @param propertyNameSchema
+     *            the schema which each property name must validate against
+     * @since 4.0
+     */
+    void setPropertyNames(Schema propertyNameSchema);
+
+    /**
+     * Sets the propertyNames property from this Schema instance. Each property <em>name</em> in the object must
+     * validate against this schema.
+     *
+     * @param propertyNameSchema
+     *            the schema which each property name must validate against
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema propertyNames(Schema propertyNameSchema) {
+        setPropertyNames(propertyNameSchema);
+        return this;
+    }
+
+    /**
+     * Returns the unevaluatedItems property of this Schema instance.
+     * <p>
+     * Items which have not successfully validated against {@code prefixItems}, {@code items}, or {@code contains} must
+     * validate against this schema.
+     *
+     * @return a schema that unevaluated array items must validate against
+     * @since 4.0
+     */
+    Schema getUnevaluatedItems();
+
+    /**
+     * Sets the unevaluatedItems property of this Schema instance.
+     * <p>
+     * Items which have not successfully validated against {@code prefixItems}, {@code items}, or {@code contains} must
+     * validate against this schema.
+     *
+     * @param unevaluatedItems
+     *            a schema that unevaluated array items must validate against
+     * @since 4.0
+     */
+    void setUnevaluatedItems(Schema unevaluatedItems);
+
+    /**
+     * Sets the unevaluatedItems property of this Schema instance.
+     * <p>
+     * Items which have not successfully validated against {@code prefixItems}, {@code items}, or {@code contains} must
+     * validate against this schema.
+     *
+     * @param unevaluatedItems
+     *            a schema that unevaluated array items must validate against
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema unevaluatedItems(Schema unevaluatedItems) {
+        setUnevaluatedItems(unevaluatedItems);
+        return this;
+    }
+
+    /**
+     * Returns the unevaluatedProperties property of this Schema instance.
+     * <p>
+     * Property values which have not successfully validated against {@code properties}, {@code patternProperties}, or
+     * {@code additionalProperties} must validate against this schema.
+     *
+     * @return a schema that unevaluated object properties must validate against
+     * @since 4.0
+     */
+    Schema getUnevaluatedProperties();
+
+    /**
+     * Sets the unevaluatedProperties property of this Schema instance.
+     * <p>
+     * Property values which have not successfully validated against {@code properties}, {@code patternProperties}, or
+     * {@code additionalProperties} must validate against this schema.
+     *
+     * @param unevaluatedProperties
+     *            a schema that unevaluated object properties must validate against
+     * @since 4.0
+     */
+    void setUnevaluatedProperties(Schema unevaluatedProperties);
+
+    /**
+     * Sets the unevaluatedProperties property of this Schema instance.
+     * <p>
+     * Property values which have not successfully validated against {@code properties}, {@code patternProperties}, or
+     * {@code additionalProperties} must validate against this schema.
+     *
+     * @param unevaluatedProperties
+     *            a schema that unevaluated object properties must validate against
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema unevaluatedProperties(Schema unevaluatedProperties) {
+        setUnevaluatedProperties(unevaluatedProperties);
+        return this;
+    }
+
+    /**
+     * Returns the const property from this Schema instance. Indicates that the object must have a specific value.
+     *
+     * @return the value that the object must have
+     * @since 4.0
+     */
+    Object getConstValue();
+
+    /**
+     * Sets the const property from this Schema instance. Indicates that the object must have a specific value.
+     *
+     * @param constValue
+     *            the value that the object must have
+     * @since 4.0
+     */
+    void setConstValue(Object constValue);
+
+    /**
+     * Sets the const property from this Schema instance. Indicates that the object must have a specific value.
+     *
+     * @param constValue
+     *            the value that the object must have
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema constValue(Object constValue) {
+        setConstValue(constValue);
+        return this;
+    }
+
+    /**
+     * Returns the maxContains property from this Schema instance. Specifies that {@code contains} must match no more
+     * than this many items in the array.
+     *
+     * @return the max number of items which may be matched by {@code contains}
+     * @since 4.0
+     */
+    Integer getMaxContains();
+
+    /**
+     * Sets the maxContains property from this Schema instance. Specifies that {@code contains} must match no more than
+     * this many items in the array.
+     *
+     * @param maxContains
+     *            the max number of items which may be matched by {@code contains}
+     * @since 4.0
+     */
+    void setMaxContains(Integer maxContains);
+
+    /**
+     * Sets the maxContains property from this Schema instance. Specifies that {@code contains} must match no more than
+     * this many items in the array.
+     *
+     * @param maxContains
+     *            the maximum number of items which may be matched by {@code contains}
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema maxContains(Integer maxContains) {
+        setMaxContains(maxContains);
+        return this;
+    }
+
+    /**
+     * Returns the minContains property from this Schema instance. Specifies that {@code contains} must match at least
+     * this many items in the array.
+     *
+     * @return the minimum number of items which may be matched by {@code contains}
+     * @since 4.0
+     */
+    Integer getMinContains();
+
+    /**
+     * Sets the minContains property from this Schema instance. Specifies that {@code contains} must match at least this
+     * many items in the array.
+     *
+     * @param minContains
+     *            the minimum number of items which may be matched by {@code contains}
+     * @since 4.0
+     */
+    void setMinContains(Integer minContains);
+
+    /**
+     * Sets the minContains property from this Schema instance. Specifies that {@code contains} must match at least this
+     * many items in the array.
+     *
+     * @param minContains
+     *            the minimum number of items which may be matched by {@code contains}
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema minContains(Integer minContains) {
+        setMinContains(minContains);
+        return this;
+    }
+
+    /**
+     * Returns the dependentRequired property of this Schema instance.
+     * <p>
+     * For each entry in the map, if the key exists as a property name in the object, then the list of names in the
+     * value must also exist as property names in the object.
+     *
+     * @return a copy Map (potentially immutable) of property names to lists of additional required property names
+     * @since 4.0
+     */
+    Map<String, List<String>> getDependentRequired();
+
+    /**
+     * Sets the dependentRequired property of this Schema instance.
+     * <p>
+     * For each entry in the map, if the key exists as a property name in the object, then the list of names in the
+     * value must also exist as property names in the object.
+     *
+     * @param dependentRequired
+     *            a map of property names to lists of additional required property names
+     * @since 4.0
+     */
+    void setDependentRequired(Map<String, List<String>> dependentRequired);
+
+    /**
+     * Sets the dependentRequired property of this Schema instance.
+     * <p>
+     * For each entry in the map, if the key exists as a property name in the object, then the list of names in the
+     * value must also exist as property names in the object.
+     *
+     * @param dependentRequired
+     *            a map of property names to lists of additional required property names
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema dependentRequired(Map<String, List<String>> dependentRequired) {
+        setDependentRequired(dependentRequired);
+        return this;
+    }
+
+    /**
+     * Sets the list of additional property names that are required if a property named {@code propertyName} exists.
+     *
+     * @param propertyName
+     *            the property name
+     * @param additionalRequiredPropertyNames
+     *            the names of additional properties which are required if {@code propertyName} exists to add
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    Schema addDependentRequired(String propertyName, List<String> additionalRequiredPropertyNames);
+
+    /**
+     * Removes the list of additional property names that are required if a property named {@code propertyName} exists.
+     *
+     * @param propertyName
+     *            the property name
+     * @since 4.0
+     */
+    void removeDependentRequired(String propertyName);
+
+    /**
+     * Returns the contentEncoding property from this Schema instance.
+     * <p>
+     * Specifies the encoding used to represent binary data as a string (e.g. base64).
+     *
+     * @return the encoding type
+     * @since 4.0
+     */
+    String getContentEncoding();
+
+    /**
+     * Sets the contentEncoding property from this Schema instance.
+     * <p>
+     * Specifies the encoding used to represent binary data as a string (e.g. base64).
+     *
+     * @param contentEncoding
+     *            the encoding type
+     * @since 4.0
+     */
+    void setContentEncoding(String contentEncoding);
+
+    /**
+     * Sets the contentEncoding property from this Schema instance.
+     * <p>
+     * Specifies the encoding used to represent binary data as a string (e.g. base64).
+     *
+     * @param contentEncoding
+     *            the encoding type
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema contentEncoding(String contentEncoding) {
+        setContentEncoding(contentEncoding);
+        return this;
+    }
+
+    /**
+     * Returns the contentMediaType property from this Schema instance.
+     * <p>
+     * Specifies the media type of the content of a string.
+     *
+     * @return the media type
+     * @since 4.0
+     */
+    String getContentMediaType();
+
+    /**
+     * Sets the contentMediaType property from this Schema instance.
+     * <p>
+     * Specifies the media type of the content of a string.
+     *
+     * @param contentMediaType
+     *            the media type
+     * @since 4.0
+     */
+    void setContentMediaType(String contentMediaType);
+
+    /**
+     * Sets the contentMediaType property from this Schema instance.
+     * <p>
+     * Specifies the media type of the content of a string.
+     *
+     * @param contentMediaType
+     *            the media type
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema contentMediaType(String contentMediaType) {
+        setContentMediaType(contentMediaType);
+        return this;
+    }
+
+    /**
+     * Returns the contentSchema property from this Schema instance.
+     * <p>
+     * If {@code contentMediaType} is a media type that maps into JSON Schema's data model, this property specifies a
+     * schema that the data in the string must conform to.
+     *
+     * @return the schema for the data within the string
+     * @since 4.0
+     */
+    Schema getContentSchema();
+
+    /**
+     * Sets the contentSchema property from this Schema instance.
+     * <p>
+     * If {@code contentMediaType} is a media type that maps into JSON Schema's data model, this property specifies a
+     * schema that the data in the string must conform to.
+     *
+     * @param contentSchema
+     *            the schema for the data within the string
+     * @since 4.0
+     */
+    void setContentSchema(Schema contentSchema);
+
+    /**
+     * Sets the contentSchema property from this Schema instance.
+     * <p>
+     * If {@code contentMediaType} is a media type that maps into JSON Schema's data model, this property specifies a
+     * schema that the data in the string must conform to.
+     *
+     * @param contentSchema
+     *            the schema for the data within the string
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema contentSchema(Schema contentSchema) {
+        setContentSchema(contentSchema);
+        return this;
+    }
+
+    /**
+     * Returns whether this Schema is a boolean schema.
+     * <p>
+     * If this property is not {@code null}, then all other properties are ignored and the schema will be represented by
+     * a boolean {@code true} or {@code false} value.
+     *
+     * @return the boolean value of this schema, or {@code null} if it is not a boolean schema
+     * @since 4.0
+     */
+    Boolean getBooleanSchema();
+
+    /**
+     * Sets this schema to a boolean value.
+     * <p>
+     * If this property is not {@code null}, then all other properties are ignored and the schema will be represented by
+     * a boolean {@code true} or {@code false} value.
+     *
+     * @param booleanSchema
+     *            the boolean value of this schema, or {@code null} if it is not a boolean schema
+     * @since 4.0
+     */
+    void setBooleanSchema(Boolean booleanSchema);
+
+    /**
+     * Sets this schema to a boolean value.
+     * <p>
+     * If this property is not {@code null}, then all other properties are ignored and the schema will be represented by
+     * a boolean {@code true} or {@code false} value.
+     *
+     * @param booleanSchema
+     *            the boolean value of this schema, or {@code null} if it is not a boolean schema
+     * @return the current Schema instance
+     * @since 4.0
+     */
+    default Schema booleanSchema(Boolean booleanSchema) {
+        setBooleanSchema(booleanSchema);
+        return this;
+    }
+
+    /**
+     * Returns the examples property of this Schema instance.
+     *
+     * @return a copy List (potentially immutable) of example objects which this schema could describe
+     * @since 4.0
+     */
+    List<Object> getExamples();
+
+    /**
+     * Sets the examples property of this Schema instance.
+     *
+     * @param examples
+     *            a list of example objects which this schema could describe
+     * @since 4.0
+     */
+    void setExamples(List<Object> examples);
+
+    /**
+     * Sets the examples property of this Schema instance.
+     *
+     * @param examples
+     *            a list of example objects which this schema could describe
+     * @return current Schema instance
+     * @since 4.0
+     */
+    default Schema examples(List<Object> examples) {
+        setExamples(examples);
+        return this;
+    }
+
+    /**
+     * Adds an example to the examples list.
+     *
+     * @param example
+     *            the example to add to the examples list
+     * @return current Schema instance
+     * @since 4.0
+     */
+    Schema addExample(Object example);
+
+    /**
+     * Removes an example from the examples list.
+     *
+     * @param example
+     *            the example to remove from the examples list
+     * @since 4.0
+     */
+    void removeExample(Object example);
+
+    /**
+     * Gets a schema property by name.
+     * <p>
+     * Allows access to arbitrary properties in a schema object, allowing use of alternative schema dialects which use
+     * different property names (or the same property names with different data types).
+     * <p>
+     * When using the standard schema dialect, this method can be used to retrieve values set by other methods. E.g.
+     *
+     * <pre>
+     * {@code
+     * schema.setMinimum(new BigDecimal(3));
+     * BigDecimal minimum = (BigDecimal) schema.get("minimum"); // returns 3
+     * }
+     * </pre>
+     *
+     * @param propertyName
+     *            the property name
+     * @return the value of the named property, or {@code null} if a property with the given name is not set
+     */
+    Object get(String propertyName);
+
+    /**
+     * Sets a schema property.
+     * <p>
+     * Allows the modifications of arbitrary schema properties in a schema properties, allowing use of alternative
+     * schema dialects which use different property names (or the same property names with different data types).
+     * <p>
+     * Passing {@code null} as the {@code value} removes the property from the schema object.
+     * <p>
+     * {@code value} must be one of the following types, otherwise non-portable behavior results:
+     * <ul>
+     * <li>Any primitive type
+     * <li>Any primitive wrapper class
+     * <li>{@code null}
+     * <li>{@code String}
+     * <li>{@code BigDecimal}
+     * <li>{@code BigInteger}
+     * <li>Any type which {@link OASFactory} can create
+     * <li>Any Enumeration
+     * <li>{@code List} where every value is a permitted type
+     * <li>{@code Map} where every key is a {@code String} and every value is a permitted type
+     * </ul>
+     *
+     * <p>
+     * When using the standard schema dialect, values set by this method can be retrieved by other methods. E.g.
+     *
+     * <pre>
+     * {@code
+     * schema.set("minimum", new BigDecimal(3));
+     * BigDecimal minimum = schema.getMinimum(); // returns 3
+     * }
+     * </pre>
+     *
+     * @param propertyName
+     *            the property name
+     * @param value
+     *            the value to set, or {@code null} to remove the property
+     * @return the current Schema instance
+     */
+    Schema set(String propertyName, Object value);
+
+    /**
+     * Gets all properties of a schema.
+     * <p>
+     * Equivalent to calling {@link #get(String)} for each property set to a non-{@code null} value and putting them all
+     * into a {@code Map}.
+     *
+     * @return a {@code Map} of property names to their corresponding values
+     */
+    Map<String, ?> getAll();
+
+    /**
+     * Sets all properties of a schema.
+     * <p>
+     * Equivalent to clearing all properties and then setting each property with {@link #set(String, Object)}.
+     *
+     * @param allProperties
+     *            the properties to set. Each value in the map must be valid according to the rules in
+     *            {@link #set(String, Object)}
+     */
+    void setAll(Map<String, ?> allProperties);
 }
diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/media/package-info.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/media/package-info.java
index a974424..3e143df 100644
--- a/api/src/main/java/org/eclipse/microprofile/openapi/models/media/package-info.java
+++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/media/package-info.java
@@ -30,6 +30,6 @@
  * </pre>
  */
 
-@org.osgi.annotation.versioning.Version("2.1")
+@org.osgi.annotation.versioning.Version("3.0")
 @org.osgi.annotation.versioning.ProviderType
 package org.eclipse.microprofile.openapi.models.media;
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 84e67b0..7f40622 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
 
     <groupId>org.eclipse.microprofile.openapi</groupId>
     <artifactId>microprofile-openapi-parent</artifactId>
-    <version>3.2-SNAPSHOT</version>
+    <version>4.0-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>MicroProfile OpenAPI</name>
     <description>Eclipse MicroProfile OpenAPI</description>
diff --git a/spec/pom.xml b/spec/pom.xml
index 05d6794..3369557 100644
--- a/spec/pom.xml
+++ b/spec/pom.xml
@@ -20,7 +20,7 @@
         <!-- This is just for now and will not work if the API has a separate release cycle than the rest. -->
         <groupId>org.eclipse.microprofile.openapi</groupId>
         <artifactId>microprofile-openapi-parent</artifactId>
-        <version>3.2-SNAPSHOT</version>
+        <version>4.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>microprofile-openapi-spec</artifactId>
diff --git a/spi/pom.xml b/spi/pom.xml
index 3d7f7a1..bc08375 100644
--- a/spi/pom.xml
+++ b/spi/pom.xml
@@ -20,7 +20,7 @@
         <!-- This is just for now and will not work if the API has a separate release cycle than the rest. -->
         <groupId>org.eclipse.microprofile.openapi</groupId>
         <artifactId>microprofile-openapi-parent</artifactId>
-        <version>3.2-SNAPSHOT</version>
+        <version>4.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>microprofile-openapi-spi</artifactId>
diff --git a/tck/pom.xml b/tck/pom.xml
index 85370ab..f2e5dfb 100644
--- a/tck/pom.xml
+++ b/tck/pom.xml
@@ -20,7 +20,7 @@
         <!-- This is just for now and will not work if the API has a separate release cycle than the rest. -->
         <groupId>org.eclipse.microprofile.openapi</groupId>
         <artifactId>microprofile-openapi-parent</artifactId>
-        <version>3.2-SNAPSHOT</version>
+        <version>4.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>microprofile-openapi-tck</artifactId>
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java b/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java
index 4769b23..45ce98a 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java
@@ -15,9 +15,14 @@
  */
 package org.eclipse.microprofile.openapi.reader;
 
+import static java.util.Collections.emptyMap;
+
 import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.DayOfWeek;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 
 import org.eclipse.microprofile.openapi.OASFactory;
@@ -38,11 +43,13 @@ import org.eclipse.microprofile.openapi.models.media.Content;
 import org.eclipse.microprofile.openapi.models.media.MediaType;
 import org.eclipse.microprofile.openapi.models.media.Schema;
 import org.eclipse.microprofile.openapi.models.parameters.Parameter;
+import org.eclipse.microprofile.openapi.models.parameters.Parameter.In;
 import org.eclipse.microprofile.openapi.models.parameters.RequestBody;
 import org.eclipse.microprofile.openapi.models.responses.APIResponse;
 import org.eclipse.microprofile.openapi.models.responses.APIResponses;
 import org.eclipse.microprofile.openapi.models.security.SecurityRequirement;
 import org.eclipse.microprofile.openapi.models.security.SecurityScheme;
+import org.eclipse.microprofile.openapi.models.security.SecurityScheme.Type;
 import org.eclipse.microprofile.openapi.models.servers.Server;
 import org.eclipse.microprofile.openapi.models.servers.ServerVariable;
 import org.eclipse.microprofile.openapi.models.tags.Tag;
@@ -90,30 +97,84 @@ public class MyOASModelReaderImpl implements OASModelReader {
                 .components(OASFactory.createObject(Components.class)
                         .schemas(new HashMap<String, Schema>())
                         .addSchema("Bookings", OASFactory.createObject(Schema.class)
-                                .type(Schema.SchemaType.INTEGER)
+                                .addType(Schema.SchemaType.INTEGER)
                                 .title("Bookings")
                                 .ref("#/components.schemas.Booking"))
                         .addSchema("Airlines", OASFactory.createObject(Schema.class)
-                                .type(Schema.SchemaType.INTEGER)
+                                .addType(Schema.SchemaType.INTEGER)
                                 .title("Airlines"))
                         .addSchema("AirlinesRef", OASFactory.createObject(Schema.class)
                                 .ref("#/components/schemas/Airlines"))
                         .addSchema("id", OASFactory.createObject(Schema.class)
-                                .type(Schema.SchemaType.INTEGER)
+                                .addType(Schema.SchemaType.INTEGER)
                                 .format("int32"))
+                        .addSchema("custom", OASFactory.createSchema()
+                                .schemaDialect("http://example.com/myCustomSchema")
+                                .set("shortKey", (short) 1)
+                                .set("intKey", 2)
+                                .set("longKey", 3L)
+                                .set("booleanKey", true)
+                                .set("charKey", 'a')
+                                .set("stringKey", "string")
+                                .set("floatKey", 3.5f)
+                                .set("doubleKey", 3.5d)
+                                .set("bigDecimalKey", new BigDecimal("3.5"))
+                                .set("bigIntegerKey", new BigInteger("7"))
+                                .set("extDocKey", OASFactory.createExternalDocumentation().description("test"))
+                                .set("operationKey", OASFactory.createOperation().description("test"))
+                                .set("pathItemKey", OASFactory.createPathItem().description("test"))
+                                .set("pathsKey", OASFactory.createPaths()
+                                        .addPathItem("test", OASFactory.createPathItem().description("test")))
+                                .set("callbackKey", OASFactory.createCallback()
+                                        .addPathItem("test", OASFactory.createPathItem().description("test")))
+                                .set("exampleKey", OASFactory.createExample().value("test"))
+                                .set("headerKey", OASFactory.createHeader().description("test"))
+                                .set("contactKey", OASFactory.createContact().name("test"))
+                                .set("infoKey", OASFactory.createInfo().title("test").version("1.0"))
+                                .set("licenseKey", OASFactory.createLicense().name("test"))
+                                .set("linkKey", OASFactory.createLink().operationId("getTestFlights"))
+                                .set("contentKey", OASFactory.createContent()
+                                        .addMediaType("test", OASFactory.createMediaType().example("test")))
+                                .set("discriminatorKey", OASFactory.createDiscriminator().propertyName("test"))
+                                .set("schemaKey", OASFactory.createSchema().title("test"))
+                                .set("xmlKey", OASFactory.createXML().name("test"))
+                                .set("parameterKey", OASFactory.createParameter().name("test").in(In.PATH))
+                                .set("requestBodyKey", OASFactory.createRequestBody()
+                                        .content(OASFactory.createContent()
+                                                .addMediaType("test", OASFactory.createMediaType().example("test"))))
+                                .set("apiResponseKey", OASFactory.createAPIResponse().description("test"))
+                                .set("apiResponsesKey", OASFactory.createAPIResponses()
+                                        .addAPIResponse("200", OASFactory.createAPIResponse().description("test")))
+                                .set("oAuthFlowKey",
+                                        OASFactory.createOAuthFlow().authorizationUrl("http://example.com"))
+                                .set("oAuthFlowsKey", OASFactory.createOAuthFlows().implicit(
+                                        OASFactory.createOAuthFlow().authorizationUrl("http://example.com")
+                                                .scopes(emptyMap())))
+                                .set("securityReqKey", OASFactory.createSecurityRequirement().addScheme("test"))
+                                .set("securitySchemeKey", OASFactory.createSecurityScheme()
+                                        .type(Type.HTTP)
+                                        .scheme("Basic"))
+                                .set("serverKey", OASFactory.createServer().url("http://example.com"))
+                                .set("serverVarKey", OASFactory.createServerVariable().defaultValue("test"))
+                                .set("tagKey", OASFactory.createTag().name("test"))
+                                .set("enumKey", DayOfWeek.MONDAY)
+                                .set("listKey", Arrays.asList(
+                                        "test",
+                                        OASFactory.createXML().name("test")))
+                                .set("mapKey", Collections.singletonMap("test", DayOfWeek.THURSDAY)))
                         .responses(new HashMap<String, APIResponse>())
                         .addResponse("FoundAirlines", OASFactory.createObject(APIResponse.class)
                                 .description("successfully found airlines")
                                 .content(OASFactory.createObject(Content.class)
                                         .addMediaType("application/json", OASFactory.createObject(MediaType.class)
                                                 .schema(OASFactory.createObject(Schema.class)
-                                                        .type(Schema.SchemaType.ARRAY)))))
+                                                        .addType(Schema.SchemaType.ARRAY)))))
                         .addResponse("FoundBookings", OASFactory.createObject(APIResponse.class)
                                 .description("Bookings retrieved")
                                 .content(OASFactory.createObject(Content.class)
                                         .addMediaType("application/json", OASFactory.createObject(MediaType.class)
                                                 .schema(OASFactory.createObject(Schema.class)
-                                                        .type(Schema.SchemaType.ARRAY)
+                                                        .addType(Schema.SchemaType.ARRAY)
                                                         .ref("#/components.schemas.Booking")))))
                         .parameters(new HashMap<String, Parameter>())
                         .addParameter("departureDate", OASFactory.createObject(Parameter.class)
@@ -144,14 +205,14 @@ public class MyOASModelReaderImpl implements OASModelReader {
                         .addHeader("Max-Rate", OASFactory.createObject(Header.class)
                                 .description("Maximum rate")
                                 .schema(OASFactory.createObject(Schema.class)
-                                        .type(Schema.SchemaType.INTEGER))
+                                        .addType(Schema.SchemaType.INTEGER))
                                 .required(true)
                                 .allowEmptyValue(true)
                                 .deprecated(true))
                         .addHeader("Request-Limit", OASFactory.createObject(Header.class)
                                 .description("The number of allowed requests in the current period")
                                 .schema(OASFactory.createObject(Schema.class)
-                                        .type(Schema.SchemaType.INTEGER)))
+                                        .addType(Schema.SchemaType.INTEGER)))
                         .securitySchemes(new HashMap<String, SecurityScheme>())
                         .addSecurityScheme("httpTestScheme", OASFactory.createObject(SecurityScheme.class)
                                 .description("user security scheme")
@@ -218,7 +279,7 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                                 .addMediaType("application/json", OASFactory
                                                                         .createObject(MediaType.class)
                                                                         .schema(OASFactory.createObject(Schema.class)
-                                                                                .type(Schema.SchemaType.ARRAY)
+                                                                                .addType(Schema.SchemaType.ARRAY)
                                                                                 .ref("#/components.schemas.Flight")))))
                                                 .addAPIResponse("404", OASFactory.createObject(APIResponse.class)
                                                         .description("No available flights found")
@@ -235,7 +296,7 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                 .allowEmptyValue(true)
                                                 .description("Airport the customer departs from")
                                                 .schema(OASFactory.createObject(Schema.class)
-                                                        .type(Schema.SchemaType.STRING)))
+                                                        .addType(Schema.SchemaType.STRING)))
                                         .addParameter(OASFactory.createObject(Parameter.class)
                                                 .name("returningDate")
                                                 .required(true)
@@ -243,14 +304,14 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                 .allowReserved(true)
                                                 .description("Customer return date")
                                                 .schema(OASFactory.createObject(Schema.class)
-                                                        .type(Schema.SchemaType.STRING)))
+                                                        .addType(Schema.SchemaType.STRING)))
                                         .addParameter(OASFactory.createObject(Parameter.class)
                                                 .name("airportTo")
                                                 .required(true)
                                                 .in(Parameter.In.QUERY)
                                                 .description("Airport the customer returns to")
                                                 .schema(OASFactory.createObject(Schema.class)
-                                                        .type(Schema.SchemaType.STRING)))
+                                                        .addType(Schema.SchemaType.STRING)))
                                         .addParameter(OASFactory.createObject(Parameter.class)
                                                 .name("numberOfAdults")
                                                 .required(true)
@@ -258,7 +319,7 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                 .description("Number of adults on the flight")
                                                 .schema(OASFactory.createObject(Schema.class)
                                                         .minimum(new BigDecimal(0))
-                                                        .type(Schema.SchemaType.STRING)))
+                                                        .addType(Schema.SchemaType.STRING)))
                                         .addParameter(OASFactory.createObject(Parameter.class)
                                                 .name("numberOfChildren")
                                                 .required(true)
@@ -267,7 +328,7 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                 .description("Number of children on the flight")
                                                 .schema(OASFactory.createObject(Schema.class)
                                                         .minimum(new BigDecimal(0))
-                                                        .type(Schema.SchemaType.STRING)))))
+                                                        .addType(Schema.SchemaType.STRING)))))
                         .addPathItem("/modelReader/bookings", OASFactory.createObject(PathItem.class)
                                 .GET(OASFactory.createObject(Operation.class)
                                         .tags(new ArrayList<String>())
@@ -281,7 +342,7 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                                 .addMediaType("applictaion/json", OASFactory
                                                                         .createObject(MediaType.class)
                                                                         .schema(OASFactory.createObject(Schema.class)
-                                                                                .type(Schema.SchemaType.ARRAY)
+                                                                                .addType(Schema.SchemaType.ARRAY)
                                                                                 .ref("#/components.schemas.Booking")))))
                                                 .addAPIResponse("404", OASFactory.createObject(APIResponse.class)
                                                         .description("No bookings found for the user"))))
@@ -304,6 +365,7 @@ public class MyOASModelReaderImpl implements OASModelReader {
                                                                         .schema(OASFactory.createObject(Schema.class)
                                                                                 .title("id")
                                                                                 .description("id of the new booking")
-                                                                                .type(Schema.SchemaType.STRING)))))))));
+                                                                                .addType(
+                                                                                        Schema.SchemaType.STRING)))))))));
     }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/utils/TCKMatchers.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/utils/TCKMatchers.java
index b819359..61e1207 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/utils/TCKMatchers.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/utils/TCKMatchers.java
@@ -15,12 +15,17 @@
  */
 package org.eclipse.microprofile.openapi.tck.utils;
 
+import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.comparator.ComparatorMatcherBuilder.comparedBy;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
 import java.util.Comparator;
 
+import org.hamcrest.Description;
 import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
 
 public final class TCKMatchers {
 
@@ -55,4 +60,113 @@ public final class TCKMatchers {
     public static Matcher<Number> comparesEqualToNumber(Number expected) {
         return comparedBy(NUMERIC_COMPARATOR).comparesEqualTo(expected);
     }
+
+    /**
+     * Creates a matcher which matches an item or a {@link Collection} containing just that item
+     *
+     * @param itemMatcher
+     *            the matcher for the item
+     * @return the matcher
+     */
+    public static Matcher<Object> itemOrSingleton(Matcher<?> itemMatcher) {
+        return new ItemOrSingletonMatcher(itemMatcher);
+    }
+
+    /**
+     * Creates a matcher which matches an item or a {@link Collection} containing just that item
+     *
+     * @param item
+     *            the item
+     * @return the matcher
+     */
+    public static Matcher<Object> itemOrSingleton(Object item) {
+        return itemOrSingleton(equalTo(item));
+    }
+
+    /**
+     * Creates a matcher which matches numbers based on their numeric value without considering their type.
+     * <p>
+     * Both the expected and actual value are converted to {@link BigDecimal} and compared using
+     * {@link BigDecimal#compareTo(BigDecimal) compareTo}.
+     *
+     * @param number
+     *            the expected number
+     * @return the matcher
+     */
+    public static Matcher<Number> number(Number number) {
+        return new NumericEqualityMatcher(number);
+    }
+
+    public static class ItemOrSingletonMatcher extends TypeSafeDiagnosingMatcher<Object> {
+
+        private Matcher<?> baseMatcher;
+
+        public ItemOrSingletonMatcher(Matcher<?> baseMatcher) {
+            super(Object.class);
+            this.baseMatcher = baseMatcher;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("An item or singleton list containing ").appendDescriptionOf(baseMatcher);
+        }
+
+        @Override
+        protected boolean matchesSafely(Object item, Description mismatchDescription) {
+            if (item instanceof Collection<?>) {
+                Collection<?> collection = (Collection<?>) item;
+                if (collection.size() != 1) {
+                    mismatchDescription.appendText("object is a collection of size ").appendValue(collection.size());
+                    return false;
+                }
+                item = collection.iterator().next();
+            }
+
+            boolean result = baseMatcher.matches(item);
+            if (!result) {
+                baseMatcher.describeMismatch(item, mismatchDescription);
+            }
+            return result;
+        }
+    }
+
+    public static class NumericEqualityMatcher extends TypeSafeDiagnosingMatcher<Number> {
+
+        private BigDecimal expected;
+
+        public NumericEqualityMatcher(Number expected) {
+            this.expected = toBigDecimal(expected);
+        }
+
+        @Override
+        public void describeTo(Description desc) {
+            desc.appendText("A number equal to ").appendValue(expected);
+        }
+
+        @Override
+        protected boolean matchesSafely(Number item, Description mismatchDescription) {
+            BigDecimal actual = toBigDecimal(item);
+            mismatchDescription.appendText("was: ").appendValue(item);
+            return expected.compareTo(actual) == 0;
+        }
+
+        private static BigDecimal toBigDecimal(Number number) {
+            if (number instanceof Integer) {
+                return new BigDecimal((Integer) number);
+            } else if (number instanceof Short) {
+                return new BigDecimal((Short) number);
+            } else if (number instanceof Long) {
+                return new BigDecimal((Long) number);
+            } else if (number instanceof Float) {
+                return new BigDecimal((Float) number);
+            } else if (number instanceof Double) {
+                return new BigDecimal((Double) number);
+            } else if (number instanceof BigInteger) {
+                return new BigDecimal((BigInteger) number);
+            } else {
+                return new BigDecimal(number.doubleValue());
+            }
+        }
+
+    }
 }
diff --git a/tck/src/main/resources/customDialect.yaml b/tck/src/main/resources/customDialect.yaml
new file mode 100644
index 0000000..e6d80a4
--- /dev/null
+++ b/tck/src/main/resources/customDialect.yaml
@@ -0,0 +1,31 @@
+# Copyright (c) 2024 Contributors to the Eclipse Foundation
+# <p>
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# <p>
+# http://www.apache.org/licenses/LICENSE-2.0
+# <p>
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+openapi: 3.1.0
+info:
+  title: Custom dialect schema example
+  version: 1.0.0
+paths:
+  /test:
+    get:
+      description: Returns data in a custom schema dialect
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $schema: http://example.com/custom
+                description: This is an example of a completely custom schema which should be passed through untouched
+                foo: bar
+                baz: qux
\ No newline at end of file
diff --git a/tck/src/main/resources/openapi.yaml b/tck/src/main/resources/openapi.yaml
index 94bc335..94016fc 100644
--- a/tck/src/main/resources/openapi.yaml
+++ b/tck/src/main/resources/openapi.yaml
@@ -30,7 +30,8 @@ paths:
           schema:
             type: string
             format: uri
-            example: https://tonys-server.com
+            examples:
+              - https://tonys-server.com
       responses:
         '201':
           description: subscription successfully created
@@ -44,7 +45,8 @@ paths:
                   subscriptionId:
                     description: this unique identifier allows management of the subscription
                     type: string
-                    example: 2531329f-fb09-4ef7-887e-84e648214436
+                    examples:
+                      - 2531329f-fb09-4ef7-887e-84e648214436
       callbacks:
         # the name `onData` is a convenience locator
         onData:
diff --git a/tck/src/main/resources/simpleapi.yaml b/tck/src/main/resources/simpleapi.yaml
index a081863..7e41499 100644
--- a/tck/src/main/resources/simpleapi.yaml
+++ b/tck/src/main/resources/simpleapi.yaml
@@ -133,14 +133,17 @@ components:
         id:
           type: string
           format: uuid
-          example: d290f1ee-6c54-4b01-90e6-d701748f0851
+          examples:
+            - d290f1ee-6c54-4b01-90e6-d701748f0851
         name:
           type: string
-          example: Widget Adapter
+          examples:
+            - Widget Adapter
         releaseDate:
           type: string
           format: int32
-          example: '2016-08-29T09:12:33.001Z'
+          examples:
+            - '2016-08-29T09:12:33.001Z'
         manufacturer:
           $ref: '#/components/schemas/Manufacturer'
     Manufacturer:
@@ -149,12 +152,15 @@ components:
       properties:
         name:
           type: string
-          example: ACME Corporation
+          examples:
+            - ACME Corporation
         homePage:
           type: string
           format: url
-          example: 'https://www.acme-corp.com'
+          examples:
+            - 'https://www.acme-corp.com'
         phone:
           type: string
-          example: 408-867-5309
+          examples:
+            - 408-867-5309
       type: object
\ No newline at end of file


Test patch:
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java
index c938d71..96b54ec 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java
@@ -16,6 +16,7 @@
 
 package org.eclipse.microprofile.openapi.tck;
 
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.itemOrSingleton;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.both;
@@ -360,7 +361,7 @@ public class AirlinesAppTest extends AppTestBase {
             vr.body(query + ".in", both(hasSize(1)).and(contains("query")));
             vr.body(query + ".description", both(hasSize(1)).and(contains(list.get(i)[1])));
             vr.body(query + ".required", both(hasSize(1)).and(contains(true)));
-            vr.body(query + ".schema.type", both(hasSize(1)).and(contains("string")));
+            vr.body(query + ".schema.type", both(hasSize(1)).and(contains(itemOrSingleton("string"))));
         }
     }
 
@@ -373,7 +374,7 @@ public class AirlinesAppTest extends AppTestBase {
                 both(hasSize(1)).and(contains("ID of the booking")));
         vr.body(reviewParameters + ".findAll { it.name == 'id' }.required", both(hasSize(1)).and(contains(true)));
         vr.body(reviewParameters + ".findAll { it.name == 'id' }.content.'*/*'.schema.type",
-                both(hasSize(1)).and(contains("integer")));
+                both(hasSize(1)).and(contains(itemOrSingleton("integer"))));
     }
 
     private void testBookingIdMethods(ValidatableResponse vr) {
@@ -386,7 +387,7 @@ public class AirlinesAppTest extends AppTestBase {
             vr.body(bookingParameters + ".findAll { it }.name", contains("id"));
             vr.body(bookingParameters + ".findAll { it.name == 'id' }.required", both(hasSize(1)).and(contains(true)));
             vr.body(bookingParameters + ".findAll { it.name == 'id' }.schema.type",
-                    both(hasSize(1)).and(contains("integer")));
+                    both(hasSize(1)).and(contains(itemOrSingleton("integer"))));
         }
 
         bookingParameters = "paths.'/bookings/{id}'.get.parameters";
@@ -415,7 +416,7 @@ public class AirlinesAppTest extends AppTestBase {
             vr.body(query + ".in", both(hasSize(1)).and(contains("query")));
             vr.body(query + ".description", both(hasSize(1)).and(contains(list.get(i)[1])));
             vr.body(query + ".required", both(hasSize(1)).and(contains(true)));
-            vr.body(query + ".schema.type", both(hasSize(1)).and(contains("string")));
+            vr.body(query + ".schema.type", both(hasSize(1)).and(contains(itemOrSingleton("string"))));
         }
 
         vr.body(availabilityParameters + ".findAll { it.name == 'numberOfAdults' }.schema.minimum",
@@ -467,13 +468,13 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(endpoint, hasKey("get"));
         vr.body(endpoint + ".get.summary", equalTo("Retrieve all bookings for current user"));
         vr.body(endpoint + ".get.responses.'200'.description", equalTo("Bookings retrieved"));
-        vr.body(endpoint + ".get.responses.'200'.content.'application/json'.schema.type", equalTo("array"));
+        vr.body(endpoint + ".get.responses.'200'.content.'application/json'.schema.type", itemOrSingleton("array"));
 
         endpoint = "paths.'/reviews'.post.callbacks.testCallback.'http://localhost:9080/oas3-airlines/reviews'";
         vr.body(endpoint, hasKey("get"));
         vr.body(endpoint + ".get.summary", equalTo("Get all reviews"));
         vr.body(endpoint + ".get.responses.'200'.description", equalTo("successful operation"));
-        vr.body(endpoint + ".get.responses.'200'.content.'application/json'.schema.type", equalTo("array"));
+        vr.body(endpoint + ".get.responses.'200'.content.'application/json'.schema.type", itemOrSingleton("array"));
         vr.body(endpoint + ".get.responses.'200'.content.'application/json'.schema.items", notNullValue());
         vr.body(endpoint + ".get.x-callback-operation", equalTo("test-callback-operation"));
     }
@@ -700,11 +701,12 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body("components.schemas.AirlinesRef.$ref", equalTo("#/components/schemas/Airlines"));
         vr.body("components.schemas.Airlines.title", equalTo("Airlines"));
         vr.body("components.schemas.Airlines.x-schema", equalTo("test-schema"));
-        vr.body("paths.'/bookings'.post.responses.'201'.content.'application/json'.schema.type", equalTo("string"));
+        vr.body("paths.'/bookings'.post.responses.'201'.content.'application/json'.schema.type",
+                itemOrSingleton("string"));
         vr.body("components.schemas.id.format", equalTo("int32"));
         vr.body("paths.'/bookings'.post.responses.'201'.content.'application/json'.schema.description",
                 equalTo("id of the new booking"));
-        vr.body("components.schemas.User.properties.password.example", equalTo("bobSm37"));
+        vr.body("components.schemas.User.properties.password.examples", contains("bobSm37"));
 
         // Object properties
         vr.body("paths.'/user'.post.requestBody.content.'application/json'.schema.maxProperties", equalTo(1024));
@@ -715,7 +717,7 @@ public class AirlinesAppTest extends AppTestBase {
 
         // Array properties
         String createSchema = "paths.'/user/createWithArray'.post.requestBody.content.'application/json'.schema";
-        vr.body(createSchema + ".nullable", equalTo(true));
+        vr.body(createSchema + ".type", containsInAnyOrder("array", "null"));
         vr.body(createSchema + ".writeOnly", equalTo(true));
         vr.body(createSchema + ".maxItems", equalTo(20));
         vr.body(createSchema + ".minItems", equalTo(2));
@@ -726,7 +728,7 @@ public class AirlinesAppTest extends AppTestBase {
     public void testSchemaProperty(String type) {
         ValidatableResponse vr = callEndpoint(type);
         vr.body("components.schemas.User.properties", IsMapWithSize.aMapWithSize(10));
-        vr.body("components.schemas.User.properties.phone.example", equalTo("123-456-7891"));
+        vr.body("components.schemas.User.properties.phone.examples", contains("123-456-7891"));
         vr.body("components.schemas.User.properties.phone.description",
                 equalTo("Telephone number to contact the user"));
         vr.body("components.schemas.User.properties.phone.x-schema-property", equalTo("test-schema-property"));
@@ -736,8 +738,8 @@ public class AirlinesAppTest extends AppTestBase {
     public void testSchemaPropertyValuesOverrideClassPropertyValues(String type) {
         ValidatableResponse vr = callEndpoint(type);
         vr.body("components.schemas.User.properties", IsMapWithSize.aMapWithSize(10));
-        vr.body("components.schemas.User.properties.phone.example", not("123-456-7890"));
-        vr.body("components.schemas.User.properties.phone.example", equalTo("123-456-7891"));
+        vr.body("components.schemas.User.properties.phone.examples", not(contains("123-456-7890")));
+        vr.body("components.schemas.User.properties.phone.examples", contains("123-456-7891"));
     }
 
     @Test(dataProvider = "formatProvider")
@@ -852,7 +854,7 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(responseHeader1 + ".deprecated", equalTo(true));
         vr.body(responseHeader1 + ".allowEmptyValue", equalTo(true));
         vr.body(responseHeader1 + ".style", equalTo("simple"));
-        vr.body(responseHeader1 + ".schema.type", equalTo("integer"));
+        vr.body(responseHeader1 + ".schema.type", itemOrSingleton("integer"));
 
         String responseHeader2 = "paths.'/reviews/{id}'.get.responses.'200'.headers.responseHeader2";
         vr.body(responseHeader2, notNullValue());
@@ -861,7 +863,7 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(responseHeader2 + ".deprecated", equalTo(true));
         vr.body(responseHeader2 + ".allowEmptyValue", equalTo(true));
         vr.body(responseHeader2 + ".style", equalTo("simple"));
-        vr.body(responseHeader2 + ".schema.type", equalTo("string"));
+        vr.body(responseHeader2 + ".schema.type", itemOrSingleton("string"));
     }
 
     @Test(dataProvider = "formatProvider")
@@ -877,7 +879,7 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(testHeader + ".deprecated", equalTo(true));
         vr.body(testHeader + ".allowEmptyValue", equalTo(true));
         vr.body(testHeader + ".style", equalTo("simple"));
-        vr.body(testHeader + ".schema.type", equalTo("integer"));
+        vr.body(testHeader + ".schema.type", itemOrSingleton("integer"));
     }
 
     @Test(dataProvider = "formatProvider")
@@ -910,7 +912,7 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(maxRate + ".deprecated", equalTo(true));
         vr.body(maxRate + ".allowEmptyValue", equalTo(true));
         vr.body(maxRate + ".style", equalTo("simple"));
-        vr.body(maxRate + ".schema.type", equalTo("integer"));
+        vr.body(maxRate + ".schema.type", itemOrSingleton("integer"));
         vr.body(maxRate + ".x-header", equalTo("test-header"));
     }
 
@@ -920,7 +922,7 @@ public class AirlinesAppTest extends AppTestBase {
 
         String content1 = "paths.'/availability'.get.responses.'200'.content.'application/json'";
         vr.body(content1, notNullValue());
-        vr.body(content1 + ".schema.type", equalTo("array"));
+        vr.body(content1 + ".schema.type", itemOrSingleton("array"));
         vr.body(content1 + ".schema.items", notNullValue());
         vr.body(content1 + ".x-content", equalTo("test-content"));
 
@@ -953,7 +955,7 @@ public class AirlinesAppTest extends AppTestBase {
         String content = "paths.'/reviews/users/{user}'.get.parameters.find{ it.name == 'user' }.content";
         vr.body(content, notNullValue());
         vr.body(content + ".'*/*'", notNullValue());
-        vr.body(content + ".'*/*'.schema.type", equalTo("string"));
+        vr.body(content + ".'*/*'.schema.type", itemOrSingleton("string"));
     }
 
     @Test(dataProvider = "formatProvider")
@@ -990,10 +992,10 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.required", equalTo(true));
         vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.description",
                 containsString("the location where data will be sent."));
-        vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.schema.type", equalTo("string"));
+        vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.schema.type", itemOrSingleton("string"));
         vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.schema.format", equalTo("uri"));
-        vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.schema.example",
-                equalTo("https://tonys-server.com"));
+        vr.body(parametersPath + ".find{ it.name == 'callbackUrl' }.schema.examples",
+                contains("https://tonys-server.com"));
 
         final String responsePath = "paths.'/streams'.post.responses";
         vr.body(responsePath, aMapWithSize(1));
@@ -1007,18 +1009,18 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(response201Path + ".content.'application/json'.schema.properties.subscriptionId.description",
                 equalTo("this unique identifier allows management of the subscription"));
         vr.body(response201Path + ".content.'application/json'.schema.properties.subscriptionId.type",
-                equalTo("string"));
-        vr.body(response201Path + ".content.'application/json'.schema.properties.subscriptionId.example",
-                equalTo("2531329f-fb09-4ef7-887e-84e648214436"));
+                itemOrSingleton("string"));
+        vr.body(response201Path + ".content.'application/json'.schema.properties.subscriptionId.examples",
+                contains("2531329f-fb09-4ef7-887e-84e648214436"));
 
         final String callbacksPath = "paths.'/streams'.post.callbacks.onData.'{$request.query.callbackUrl}/data'.post";
         vr.body(callbacksPath + ".requestBody.description", equalTo("subscription payload"));
         vr.body(callbacksPath + ".requestBody.content.'application/json'.schema.properties.timestamp.type",
-                equalTo("string"));
+                itemOrSingleton("string"));
         vr.body(callbacksPath + ".requestBody.content.'application/json'.schema.properties.timestamp.format",
                 equalTo("date-time"));
         vr.body(callbacksPath + ".requestBody.content.'application/json'.schema.properties.userData.type",
-                equalTo("string"));
+                itemOrSingleton("string"));
 
         vr.body(callbacksPath + ".responses", aMapWithSize(2));
         vr.body(callbacksPath + ".responses.'202'.description",
@@ -1055,7 +1057,7 @@ public class AirlinesAppTest extends AppTestBase {
 
         String rejectedReviewSchema =
                 dereference(vr, "paths.'/reviews'.post.responses.'400'.content.'application/json'.schema");
-        vr.body(rejectedReviewSchema + ".type", equalTo("object"));
+        vr.body(rejectedReviewSchema + ".type", itemOrSingleton("object"));
         vr.body(rejectedReviewSchema + ".properties", hasKey("reason"));
     }
 
@@ -1104,7 +1106,7 @@ public class AirlinesAppTest extends AppTestBase {
         vr.body(responseSchema, notNullValue());
         String flightSchema = dereference(vr, responseSchema, "properties.returningFlight");
 
-        vr.body(flightSchema + ".additionalProperties.type", equalTo("string"));
+        vr.body(flightSchema + ".additionalProperties.type", itemOrSingleton("string"));
     }
 
     @Test(dataProvider = "formatProvider")
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/FilterTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/FilterTest.java
index dbc4abf..70d11bd 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/FilterTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/FilterTest.java
@@ -16,6 +16,7 @@
 
 package org.eclipse.microprofile.openapi.tck;
 
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.itemOrSingleton;
 import static org.hamcrest.Matchers.both;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
@@ -112,7 +113,7 @@ public class FilterTest extends AppTestBase {
         vr.body(username + ".in", both(hasSize(1)).and(contains("query")));
         vr.body(username + ".description", both(hasSize(1)).and(contains("filterParameter - The user name for login")));
         vr.body(username + ".required", both(hasSize(1)).and(contains(true)));
-        vr.body(username + ".schema.type", both(hasSize(1)).and(contains("string")));
+        vr.body(username + ".schema.type", both(hasSize(1)).and(contains(itemOrSingleton("string"))));
 
         // Parameter named 'password' should have been removed by filter
         vr.body(reviewParameters, hasSize(1));
@@ -161,7 +162,7 @@ public class FilterTest extends AppTestBase {
         vr.body(maxRate + ".deprecated", equalTo(true));
         vr.body(maxRate + ".allowEmptyValue", equalTo(true));
         vr.body(maxRate + ".style", equalTo("simple"));
-        vr.body(maxRate + ".schema.type", equalTo("integer"));
+        vr.body(maxRate + ".schema.type", itemOrSingleton("integer"));
     }
 
     @Test(dataProvider = "formatProvider")
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
index aadb17d..dd73606 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelConstructionTest.java
@@ -20,6 +20,9 @@ package org.eclipse.microprofile.openapi.tck;
 // import static org.testng.Assert.assertNotSame;
 // import static org.testng.Assert.assertSame;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.testng.Assert.assertEquals;
@@ -40,6 +43,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -166,7 +170,7 @@ public class ModelConstructionTest extends Arquillian {
             try {
                 return getter.invoke(target);
             } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                fail("Invocation of getter method \"" + getter.getName() + "\" failed: " + e.getMessage());
+                fail("Invocation of getter method \"" + getter.getName() + "\" failed: " + e.getMessage(), e);
                 throw new RuntimeException(e);
             }
         }
@@ -174,7 +178,7 @@ public class ModelConstructionTest extends Arquillian {
             try {
                 setter.invoke(target, value);
             } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                fail("Invocation of setter method \"" + setter.getName() + "\" failed: " + e.getMessage());
+                fail("Invocation of setter method \"" + setter.getName() + "\" failed: " + e.getMessage(), e);
                 throw new RuntimeException(e);
             }
         }
@@ -182,7 +186,7 @@ public class ModelConstructionTest extends Arquillian {
             try {
                 return builder.invoke(target, value);
             } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
-                fail("Invocation of builder method \"" + builder.getName() + "\" failed: " + e.getMessage());
+                fail("Invocation of builder method \"" + builder.getName() + "\" failed: " + e.getMessage(), e);
                 throw new RuntimeException(e);
             }
         }
@@ -990,6 +994,7 @@ public class ModelConstructionTest extends Arquillian {
         checkNullValueInAdd(mt::getExamples, mt::addExample, "otherExample", exampleValue);
     }
 
+    @SuppressWarnings("deprecation") // Testing deprecated Schema methods
     @Test
     public void schemaTest() {
         final Schema s = processConstructible(Schema.class);
@@ -999,22 +1004,40 @@ public class ModelConstructionTest extends Arquillian {
         checkSameObject(ap, s.getAdditionalPropertiesSchema());
         assertEquals(s.getAdditionalPropertiesBoolean(), null,
                 "AdditionalProperties (Boolean type) is expected to be null");
+
         checkSameObject(s, s.additionalPropertiesBoolean(Boolean.TRUE));
         assertEquals(s.getAdditionalPropertiesBoolean(), Boolean.TRUE,
                 "AdditionalProperties (Boolean type) is expected to be true");
-        assertEquals(s.getAdditionalPropertiesSchema(), null,
-                "AdditionalProperties (Schema type) is expected to be null");
+        Schema s2 = s.getAdditionalPropertiesSchema();
+        assertNotNull(s2, "AdditionalProperties (Schema type) is expected to be non-null");
+        assertEquals(s2.getBooleanSchema(), Boolean.TRUE,
+                "AdditionalProperties (Schema type) is expected to return a boolean-true schema");
+
         s.setAdditionalPropertiesBoolean(Boolean.FALSE);
         assertEquals(s.getAdditionalPropertiesBoolean(), Boolean.FALSE,
                 "AdditionalProperties (Boolean type) is expected to be false");
-        assertEquals(s.getAdditionalPropertiesSchema(), null,
-                "AdditionalProperties (Schema type) is expected to be null");
+        s2 = s.getAdditionalPropertiesSchema();
+        assertNotNull(s2, "AdditionalProperties (Schema type) is expected to be non-null");
+        assertEquals(s2.getBooleanSchema(), Boolean.FALSE,
+                "AdditionalProperties (Schema type) is expected to return a boolean-false schema");
+
         s.setAdditionalPropertiesSchema(null);
         assertEquals(s.getAdditionalPropertiesBoolean(), null,
                 "AdditionalProperties (Boolean type) is expected to be null");
         assertEquals(s.getAdditionalPropertiesSchema(), null,
                 "AdditionalProperties (Schema type) is expected to be null");
 
+        s.setExamples(null);
+        s.setExample("example1");
+        assertEquals(s.getExample(), "example1", "Example is expected to be set");
+        assertNull(s.getExamples(), "Examples should be null");
+        s.setExamples(Arrays.asList("example2", "example3"));
+        assertEquals(s.getExample(), "example1", "Example should not be affected by settings examples");
+        assertThat("Examples should be set", s.getExamples(), contains("example2", "example3"));
+        s.setExample("example4");
+        assertEquals(s.getExample(), "example4", "Example should be set");
+        assertThat("Examples should not be affected by example", s.getExamples(), contains("example2", "example3"));
+
         final Schema allOf = createConstructibleInstance(Schema.class);
         checkSameObject(s, s.addAllOf(allOf));
         checkListEntry(s.getAllOf(), allOf);
@@ -1124,7 +1147,285 @@ public class ModelConstructionTest extends Arquillian {
         checkListEntry(s.getRequired(), required);
 
         final String otherRequiredValue = new String("otherRequired");
-        checkListImmutable(s, Schema::getEnumeration, otherRequiredValue);
+        checkListImmutable(s, Schema::getRequired, otherRequiredValue);
+
+        final String dependentSchemaKey = "myDependentSchemaKey";
+        final Schema dependentSchemaValue = createConstructibleInstance(Schema.class);
+        checkSameObject(s, s.addDependentSchema(dependentSchemaKey, dependentSchemaValue));
+        checkMapEntry(s.getDependentSchemas(), dependentSchemaKey, dependentSchemaValue);
+        assertEquals(s.getDependentSchemas().size(), 1, "The map is expected to contain one entry.");
+        s.removeDependentSchema(dependentSchemaKey);
+        assertEquals(s.getDependentSchemas().size(), 0, "The map is expected to be empty.");
+
+        final String dependentSchemaKey2 = "myDependentSchemaKey2";
+        final Schema dependentSchemaValue2 = createConstructibleInstance(Schema.class);
+        s.setDependentSchemas(Collections.singletonMap(dependentSchemaKey2, dependentSchemaValue2));
+        checkMapEntry(s.getDependentSchemas(), dependentSchemaKey2, dependentSchemaValue2);
+        assertEquals(s.getDependentSchemas().size(), 1, "The map is expected to contain one entry.");
+        checkSameObject(s, s.addDependentSchema(dependentSchemaKey, dependentSchemaValue));
+        checkMapEntry(s.getDependentSchemas(), dependentSchemaKey, dependentSchemaValue);
+        assertEquals(s.getDependentSchemas().size(), 2, "The map is expected to contain two entries.");
+
+        final Schema otherDependentSchemaValue = createConstructibleInstance(Schema.class);
+        checkMapImmutable(s, Schema::getDependentSchemas, "otherDependentSchemaKey", otherDependentSchemaValue);
+        checkNullValueInAdd(s::getDependentSchemas, s::addDependentSchema, "otherDependentSchemaKey",
+                dependentSchemaValue);
+
+        final Schema prefixItem = createConstructibleInstance(Schema.class);
+        checkSameObject(s, s.addPrefixItem(prefixItem));
+        checkListEntry(s.getPrefixItems(), prefixItem);
+        assertEquals(s.getPrefixItems().size(), 1, "The list is expected to contain one entry.");
+        s.removePrefixItem(prefixItem);
+        assertEquals(s.getPrefixItems().size(), 0, "The list is expected to be empty.");
+
+        final Schema prefixItem2 = createConstructibleInstance(Schema.class);
+        s.setPrefixItems(Collections.singletonList(prefixItem2));
+        assertEquals(s.getPrefixItems().size(), 1, "The list is expected to contain one entry.");
+        checkListEntry(s.getPrefixItems(), prefixItem2);
+        checkSameObject(s, s.addPrefixItem(prefixItem));
+        assertEquals(s.getPrefixItems().size(), 2, "The list is expected to contain two entries.");
+        checkListEntry(s.getPrefixItems(), prefixItem);
+
+        final Schema otherPrefixItemValue = createConstructibleInstance(Schema.class);
+        checkListImmutable(s, Schema::getPrefixItems, otherPrefixItemValue);
+
+        final String patternPropertyKey = "myPatternPropertyKey";
+        final Schema patternPropertyValue = createConstructibleInstance(Schema.class);
+        checkSameObject(s, s.addPatternProperty(patternPropertyKey, patternPropertyValue));
+        checkMapEntry(s.getPatternProperties(), patternPropertyKey, patternPropertyValue);
+        assertEquals(s.getPatternProperties().size(), 1, "The map is expected to contain one entry.");
+        s.removePatternProperty(patternPropertyKey);
+        assertEquals(s.getPatternProperties().size(), 0, "The map is expected to be empty.");
+
+        final String patternPropertyKey2 = "myPatternPropertyKey2";
+        final Schema patternPropertyValue2 = createConstructibleInstance(Schema.class);
+        s.setPatternProperties(Collections.singletonMap(patternPropertyKey2, patternPropertyValue2));
+        checkMapEntry(s.getPatternProperties(), patternPropertyKey2, patternPropertyValue2);
+        assertEquals(s.getPatternProperties().size(), 1, "The map is expected to contain one entry.");
+        checkSameObject(s, s.addPatternProperty(patternPropertyKey, patternPropertyValue));
+        checkMapEntry(s.getPatternProperties(), patternPropertyKey, patternPropertyValue);
+        assertEquals(s.getPatternProperties().size(), 2, "The map is expected to contain two entries.");
+
+        final Schema otherPatternPropertyValue = createConstructibleInstance(Schema.class);
+        checkMapImmutable(s, Schema::getPatternProperties, "otherPatternPropertyKey", otherPatternPropertyValue);
+        checkNullValueInAdd(s::getPatternProperties, s::addPatternProperty, "otherPatternPropertyKey",
+                patternPropertyValue);
+
+        final String dependentRequiredKey = "myDependentRequiredKey";
+        final List<String> dependentRequiredValue = Collections.singletonList("myDependentRequired");
+        checkSameObject(s, s.addDependentRequired(dependentRequiredKey, dependentRequiredValue));
+        checkMapEntry(s.getDependentRequired(), dependentRequiredKey, dependentRequiredValue);
+        assertEquals(s.getDependentRequired().size(), 1, "The map is expected to contain one entry.");
+        s.removeDependentRequired(dependentRequiredKey);
+        assertEquals(s.getDependentRequired().size(), 0, "The map is expected to be empty.");
+
+        final String dependentRequiredKey2 = "myDependentRequiredKey2";
+        final List<String> dependentRequiredValue2 = Collections.singletonList("myDependentRequired2");
+        s.setDependentRequired(Collections.singletonMap(dependentRequiredKey2, dependentRequiredValue2));
+        checkMapEntry(s.getDependentRequired(), dependentRequiredKey2, dependentRequiredValue2);
+        assertEquals(s.getDependentRequired().size(), 1, "The map is expected to contain one entry.");
+        checkSameObject(s, s.addDependentRequired(dependentRequiredKey, dependentRequiredValue));
+        checkMapEntry(s.getDependentRequired(), dependentRequiredKey, dependentRequiredValue);
+        assertEquals(s.getDependentRequired().size(), 2, "The map is expected to contain two entries.");
+
+        final List<String> otherDependentRequiredValue = Collections.singletonList("myOtherDependentRequired");
+        checkMapImmutable(s, Schema::getDependentRequired, "otherDependentRequiredKey", otherDependentRequiredValue);
+        checkNullValueInAdd(s::getDependentRequired, s::addDependentRequired, "otherDependentRequiredKey",
+                dependentRequiredValue);
+    }
+
+    @SuppressWarnings("deprecation") // Testing deprecated Schema methods
+    @Test
+    public void testSchemaArbitraryProperties() {
+        Schema s = createConstructibleInstance(Schema.class);
+
+        testSchemaProperty(s, "discriminator", Schema::getDiscriminator, Schema::setDiscriminator,
+                createConstructibleInstance(Discriminator.class));
+        testSchemaProperty(s, "title", Schema::getTitle, Schema::setTitle, "test title");
+        testSchemaProperty(s, "default", Schema::getDefaultValue, Schema::setDefaultValue, "test");
+        testSchemaListProperty(s, "enum", Schema::getEnumeration, Schema::setEnumeration, "a");
+        testSchemaProperty(s, "multipleOf", Schema::getMultipleOf, Schema::setMultipleOf, new BigDecimal("3"));
+        testSchemaProperty(s, "maximum", Schema::getMaximum, Schema::setMaximum, new BigDecimal("3"));
+        testSchemaProperty(s, "exclusiveMaximum", Schema::getExclusiveMaximum, Schema::setExclusiveMaximum,
+                new BigDecimal("3"));
+        testSchemaProperty(s, "minimum", Schema::getMinimum, Schema::setMinimum, new BigDecimal("3"));
+        testSchemaProperty(s, "exclusiveMinimum", Schema::getExclusiveMinimum, Schema::setExclusiveMinimum,
+                new BigDecimal("3"));
+        testSchemaProperty(s, "maxLength", Schema::getMaxLength, Schema::setMaxLength, 17);
+        testSchemaProperty(s, "minLength", Schema::getMinLength, Schema::setMinLength, 5);
+        testSchemaProperty(s, "pattern", Schema::getPattern, Schema::setPattern, "[a-z]+");
+        testSchemaProperty(s, "maxItems", Schema::getMaxItems, Schema::setMaxItems, 5);
+        testSchemaProperty(s, "minItems", Schema::getMinItems, Schema::setMinItems, 3);
+        testSchemaProperty(s, "uniqueItems", Schema::getUniqueItems, Schema::setUniqueItems, true);
+        testSchemaProperty(s, "maxProperties", Schema::getMaxProperties, Schema::setMaxProperties, 10);
+        testSchemaProperty(s, "minProperties", Schema::getMinProperties, Schema::setMinProperties, 8);
+        testSchemaListProperty(s, "required", Schema::getRequired, Schema::setRequired, "propName");
+        testSchemaListProperty(s, "type", Schema::getType, Schema::setType, Schema.SchemaType.OBJECT);
+        testSchemaProperty(s, "not", Schema::getNot, Schema::setNot, createConstructibleInstance(Schema.class));
+        testSchemaMapProperty(s, "properties", Schema::getProperties, Schema::setProperties,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "additionalProperties", Schema::getAdditionalPropertiesSchema,
+                Schema::setAdditionalPropertiesSchema,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "description", Schema::getDescription, Schema::setDescription, "test schema");
+        testSchemaProperty(s, "format", Schema::getFormat, Schema::setFormat, "date-time");
+        testSchemaProperty(s, "readOnly", Schema::getReadOnly, Schema::setReadOnly, true);
+        testSchemaProperty(s, "writeOnly", Schema::getWriteOnly, Schema::setWriteOnly, true);
+        testSchemaProperty(s, "example", Schema::getExample, Schema::setExample, "test");
+        testSchemaProperty(s, "externalDocs", Schema::getExternalDocs, Schema::setExternalDocs,
+                createConstructibleInstance(ExternalDocumentation.class));
+        testSchemaProperty(s, "deprecated", Schema::getDeprecated, Schema::setDeprecated, true);
+        testSchemaProperty(s, "xml", Schema::getXml, Schema::setXml, createConstructibleInstance(XML.class));
+        testSchemaProperty(s, "items", Schema::getItems, Schema::setItems, createConstructibleInstance(Schema.class));
+        testSchemaListProperty(s, "allOf", Schema::getAllOf, Schema::setAllOf,
+                createConstructibleInstance(Schema.class));
+        testSchemaListProperty(s, "anyOf", Schema::getAnyOf, Schema::setAnyOf,
+                createConstructibleInstance(Schema.class));
+        testSchemaListProperty(s, "oneOf", Schema::getOneOf, Schema::setOneOf,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "$schema", Schema::getSchemaDialect, Schema::setSchemaDialect, "http://test.dialect");
+        testSchemaProperty(s, "$comment", Schema::getComment, Schema::setComment, "about this schema");
+        testSchemaProperty(s, "if", Schema::getIfSchema, Schema::setIfSchema,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "then", Schema::getThenSchema, Schema::setThenSchema,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "else", Schema::getElseSchema, Schema::setElseSchema,
+                createConstructibleInstance(Schema.class));
+        testSchemaMapProperty(s, "dependentSchemas", Schema::getDependentSchemas, Schema::setDependentSchemas,
+                createConstructibleInstance(Schema.class));
+        testSchemaListProperty(s, "prefixItems", Schema::getPrefixItems, Schema::setPrefixItems,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "contains", Schema::getContains, Schema::setContains,
+                createConstructibleInstance(Schema.class));
+        testSchemaMapProperty(s, "patternProperties", Schema::getPatternProperties, Schema::setPatternProperties,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "propertyNames", Schema::getPropertyNames, Schema::setPropertyNames,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "unevaluatedItems", Schema::getUnevaluatedItems, Schema::setUnevaluatedItems,
+                createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "unevaluatedProperties", Schema::getUnevaluatedProperties,
+                Schema::setUnevaluatedProperties, createConstructibleInstance(Schema.class));
+        testSchemaProperty(s, "const", Schema::getConstValue, Schema::setConstValue, "value");
+        testSchemaProperty(s, "maxContains", Schema::getMaxContains, Schema::setMaxContains, 5);
+        testSchemaProperty(s, "minContains", Schema::getMinContains, Schema::setMinContains, 3);
+        testSchemaMapProperty(s, "dependentRequired", Schema::getDependentRequired, Schema::setDependentRequired,
+                Arrays.asList("a", "b"));
+        testSchemaProperty(s, "contentEncoding", Schema::getContentEncoding, Schema::setContentEncoding, "base64");
+        testSchemaProperty(s, "contentMediaType", Schema::getContentMediaType, Schema::setContentMediaType,
+                "test/plain");
+        testSchemaProperty(s, "contentSchema", Schema::getContentSchema, Schema::setContentSchema,
+                createConstructibleInstance(Schema.class));
+        testSchemaListProperty(s, "examples", Schema::getExamples, Schema::setExamples, "foo");
+    }
+
+    public <V> void testSchemaProperty(Schema testSchema, String name, Function<Schema, V> getter,
+            BiConsumer<Schema, V> setter, V testValue) {
+        // Set with the setter
+        setter.accept(testSchema, testValue);
+        assertSame(getter.apply(testSchema), testValue,
+                "Getter should return the same instance as was set for property " + name);
+        assertSame(testSchema.get(name), testValue,
+                "Generic getter should return same instance as was set for property " + name);
+
+        // Clear with the setter
+        setter.accept(testSchema, null);
+        assertNull(getter.apply(testSchema), "Getter should return null for property " + name);
+        assertNull(testSchema.get(name), "Generic access should return null for property " + name);
+
+        // Set with generic access
+        testSchema.set(name, testValue);
+        assertSame(getter.apply(testSchema), testValue,
+                "Getter should return the same instance as was set for property " + name);
+        assertSame(testSchema.get(name), testValue,
+                "Generic getter should return same instance as was set for property " + name);
+
+        // Clear with generic access
+        testSchema.set(name, null);
+        assertNull(getter.apply(testSchema), "Getter should return null for property " + name);
+        assertNull(testSchema.get(name), "Generic access should return null for property " + name);
+    }
+
+    public <V> void testSchemaMapProperty(Schema testSchema, String name,
+            Function<Schema, Map<String, V>> getter, BiConsumer<Schema, Map<String, V>> setter,
+            V testValue) {
+        // Set with the setter
+        Map<String, V> testMap = new HashMap<>();
+        testMap.put("test1", testValue);
+
+        setter.accept(testSchema, testMap);
+        testMapFromGetter(() -> getter.apply(testSchema), testMap, name);
+        testMapFromGetter(() -> testSchema.get(name), testMap, name);
+
+        // Clear with the setter
+        setter.accept(testSchema, null);
+        assertNull(getter.apply(testSchema), "Getter should return null for property " + name);
+        assertNull(testSchema.get(name), "Generic access should return null for property " + name);
+
+        // Set with generic access
+        testSchema.set(name, testMap);
+        testMapFromGetter(() -> getter.apply(testSchema), testMap, name);
+        testMapFromGetter(() -> testSchema.get(name), testMap, name);
+
+        // Clear with generic access
+        testSchema.set(name, null);
+        assertNull(getter.apply(testSchema), "Getter should return null for property " + name);
+        assertNull(testSchema.get(name), "Generic access should return null for property " + name);
+    }
+
+    private <V> void testMapFromGetter(Supplier<Object> getter, Map<String, V> expected,
+            String name) {
+        assertThat("Test error: expected may not be empty", expected, not(anEmptyMap()));
+
+        Object actualObj = getter.get();
+        assertEquals(actualObj, expected, "Getter should return value that was set for property " + name);
+
+        // Check that all values are the same instance
+        Map<?, ?> actualMap = (Map<?, ?>) actualObj;
+        expected.forEach((k, v) -> {
+            assertSame(v, actualMap.get(k),
+                    "Getter should return value with same instance for key " + v + " property " + name);
+        });
+    }
+
+    public <V> void testSchemaListProperty(Schema testSchema, String name,
+            Function<Schema, List<V>> getter, BiConsumer<Schema, List<V>> setter,
+            V testValue) {
+        // Set with the setter
+        List<V> testList = new ArrayList<>();
+        testList.add(testValue);
+
+        setter.accept(testSchema, testList);
+        testListFromGetter(() -> getter.apply(testSchema), testList, name);
+        testListFromGetter(() -> testSchema.get(name), testList, name);
+
+        // Clear with the setter
+        setter.accept(testSchema, null);
+        assertNull(getter.apply(testSchema), "Getter should return null for property " + name);
+        assertNull(testSchema.get(name), "Generic access should return null for property " + name);
+
+        // Set with generic access
+        testSchema.set(name, testList);
+        testListFromGetter(() -> getter.apply(testSchema), testList, name);
+        testListFromGetter(() -> testSchema.get(name), testList, name);
+
+        // Clear with generic access
+        testSchema.set(name, null);
+        assertNull(getter.apply(testSchema), "Getter should return null for property " + name);
+        assertNull(testSchema.get(name), "Generic access should return null for property " + name);
+    }
+
+    private <V> void testListFromGetter(Supplier<Object> getter, List<V> expected, String name) {
+        assertThat("Test error: expected may not be empty", expected, not(empty()));
+
+        Object actualObj = getter.get();
+        assertEquals(actualObj, expected, "Getter should return value that was set for property " + name);
+
+        // Check that all values are the same instance
+        List<?> actualList = (List<?>) actualObj;
+        expected.forEach((expectedV) -> {
+            assertTrue(actualList.stream().anyMatch((actualV) -> expectedV == actualV),
+                    "Getter should return value containing " + expectedV + " for property " + name);
+        });
     }
 
     @Test
@@ -1524,6 +1825,7 @@ public class ModelConstructionTest extends Arquillian {
                         "\" is expected to be equal to the value that was set.");
             }
         }
+        p.invokeSetter(o, null);
     }
 
     // Returns instances for testing getter, setter and builder methods.
@@ -1546,21 +1848,21 @@ public class ModelConstructionTest extends Arquillian {
         } else if (clazz == String.class) {
             return new String("value");
         } else if (clazz == Boolean.class || clazz == Boolean.TYPE) {
-            return new Boolean(true);
+            return Boolean.valueOf(true);
         } else if (clazz == Byte.class || clazz == Byte.TYPE) {
-            return new Byte((byte) 1);
+            return Byte.valueOf((byte) 1);
         } else if (clazz == Short.class || clazz == Short.TYPE) {
-            return new Short((short) 1);
+            return Short.valueOf((short) 1);
         } else if (clazz == Integer.class || clazz == Integer.TYPE) {
-            return new Integer(1);
+            return Integer.valueOf(1);
         } else if (clazz == Long.class || clazz == Long.TYPE) {
-            return new Long(1L);
+            return Long.valueOf(1L);
         } else if (clazz == Float.class || clazz == Float.TYPE) {
-            return new Float(1);
+            return Float.valueOf(1);
         } else if (clazz == Double.class || clazz == Double.TYPE) {
-            return new Double(1);
+            return Double.valueOf(1);
         } else if (clazz == Character.class || clazz == Character.TYPE) {
-            return new Character('a');
+            return Character.valueOf('a');
         } else if (clazz == BigInteger.class) {
             return new BigInteger("1");
         } else if (clazz == BigDecimal.class) {
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java
index cfdf643..7c638c7 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java
@@ -16,11 +16,15 @@
 
 package org.eclipse.microprofile.openapi.tck;
 
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.itemOrSingleton;
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.number;
 import static org.hamcrest.Matchers.both;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.hasItems;
 import static org.hamcrest.Matchers.hasKey;
 import static org.hamcrest.Matchers.hasSize;
@@ -169,7 +173,7 @@ public class ModelReaderAppTest extends AppTestBase {
             vr.body(query + ".in", both(hasSize(1)).and(contains("query")));
             vr.body(query + ".description", both(hasSize(1)).and(contains(list.get(i)[1])));
             vr.body(query + ".required", both(hasSize(1)).and(contains(true)));
-            vr.body(query + ".schema.type", both(hasSize(1)).and(contains("string")));
+            vr.body(query + ".schema.type", both(hasSize(1)).and(contains(itemOrSingleton("string"))));
         }
 
         vr.body(availabilityParameters + ".findAll { it.name == 'numberOfAdults' }.schema.minimum",
@@ -211,12 +215,58 @@ public class ModelReaderAppTest extends AppTestBase {
         vr.body("components.schemas.AirlinesRef.$ref", equalTo("#/components/schemas/Airlines"));
         vr.body("components.schemas.Airlines.title", equalTo("Airlines"));
         vr.body("paths.'/modelReader/bookings'.post.responses.'201'.content.'text/plain'.schema.type",
-                equalTo("string"));
+                itemOrSingleton("string"));
         vr.body("components.schemas.id.format", equalTo("int32"));
         vr.body("paths.'/modelReader/bookings'.post.responses.'201'.content.'text/plain'.schema.description",
                 equalTo("id of the new booking"));
     }
 
+    @Test(dataProvider = "formatProvider")
+    public void testSchemaCustomProperties(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+        vr.body("components.schemas.custom.$schema", equalTo("http://example.com/myCustomSchema"));
+        vr.body("components.schemas.custom.shortKey", number(1));
+        vr.body("components.schemas.custom.intKey", number(2));
+        vr.body("components.schemas.custom.longKey", number(3));
+        vr.body("components.schemas.custom.booleanKey", equalTo(true));
+        vr.body("components.schemas.custom.charKey", equalTo("a"));
+        vr.body("components.schemas.custom.stringKey", equalTo("string"));
+        vr.body("components.schemas.custom.floatKey", number(3.5));
+        vr.body("components.schemas.custom.doubleKey", number(3.5));
+        vr.body("components.schemas.custom.bigDecimalKey", number(3.5));
+        vr.body("components.schemas.custom.bigIntegerKey", number(7));
+        vr.body("components.schemas.custom.extDocKey.description", equalTo("test"));
+        vr.body("components.schemas.custom.operationKey.description", equalTo("test"));
+        vr.body("components.schemas.custom.pathItemKey.description", equalTo("test"));
+        vr.body("components.schemas.custom.pathsKey.test.description", equalTo("test"));
+        vr.body("components.schemas.custom.callbacKey.description", equalTo("test"));
+        vr.body("components.schemas.custom.exampleKey.value", equalTo("test"));
+        vr.body("components.schemas.custom.headerKey.description", equalTo("test"));
+        vr.body("components.schemas.custom.contactKey.name", equalTo("test"));
+        vr.body("components.schemas.custom.infoKey.title", equalTo("test"));
+        vr.body("components.schemas.custom.licenseKey.name", equalTo("test"));
+        vr.body("components.schemas.custom.linkKey.operationId", equalTo("test"));
+        vr.body("components.schemas.custom.contentKey.test.example", equalTo("test"));
+        vr.body("components.schemas.custom.discriminatorKey.propertyName", equalTo("test"));
+        vr.body("components.schemas.custom.schemaKey.title", equalTo("test"));
+        vr.body("components.schemas.custom.xmlKey.name", equalTo("test"));
+        vr.body("components.schemas.custom.parameterKey.name", equalTo("test"));
+        vr.body("components.schemas.custom.requestBodyKey.content.test.example", equalTo("test"));
+        vr.body("components.schemas.custom.apiResponseKey.description", equalTo("test"));
+        vr.body("components.schemas.custom.apiResponsesKey.200.description", equalTo("test"));
+        vr.body("components.schemas.custom.oAuthFlowKey.authorizationUrl", equalTo("http://example.com"));
+        vr.body("components.schemas.custom.oAuthFlowsKey.implicit.authorizationUrl", equalTo("http://example.com"));
+        vr.body("components.schemas.custom.securityReqKey.scheme", contains("test"));
+        vr.body("components.schemas.custom.securitySchemeKey.type", equalTo("http"));
+        vr.body("components.schemas.custom.serverKey.url", equalTo("http://example.com"));
+        vr.body("components.schemas.custom.serverVarKey.default", equalTo("test"));
+        vr.body("components.schemas.custom.tagKey.name", equalTo("test"));
+        vr.body("components.schemas.custom.enumKey", equalToIgnoringCase("MONDAY"));
+        vr.body("components.schemas.custom.listKey", hasItem("test"));
+        vr.body("components.schemas.custom.listKey.name", hasItem("test"));
+        vr.body("components.schemas.custom.mapKey.test", equalToIgnoringCase("MONDAY"));
+    }
+
     @Test(dataProvider = "formatProvider")
     public void testExampleObject(String type) {
         ValidatableResponse vr = callEndpoint(type);
@@ -291,7 +341,7 @@ public class ModelReaderAppTest extends AppTestBase {
 
         String content1 = "paths.'/availability'.get.responses.'200'.content.'application/json'";
         vr.body(content1, notNullValue());
-        vr.body(content1 + ".schema.type", equalTo("array"));
+        vr.body(content1 + ".schema.type", itemOrSingleton("array"));
         vr.body(content1 + ".schema.items", notNullValue());
     }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASConfigSchemaTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASConfigSchemaTest.java
index e42285b..e5b5194 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASConfigSchemaTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/OASConfigSchemaTest.java
@@ -16,14 +16,17 @@
 
 package org.eclipse.microprofile.openapi.tck;
 
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.itemOrSingleton;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
 
 import java.util.Map;
 
 import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
@@ -50,12 +53,14 @@ public class OASConfigSchemaTest extends AppTestBase {
                 anyOf(epochSecondsSchema(), epochSecondsRef()));
     }
 
-    private Matcher<Map<? extends String, ? extends String>> epochSecondsSchema() {
-        return allOf(aMapWithSize(4),
+    private Matcher<Map<? extends String, ?>> epochSecondsSchema() {
+        return Matchers.<Map<? extends String, ?>>allOf(
+                aMapWithSize(4),
                 hasEntry("title", "Epoch Seconds"),
-                hasEntry("type", "number"),
+                hasEntry(is("type"), itemOrSingleton("number")),
                 hasEntry("format", "int64"),
-                hasEntry("description", "Number of seconds from the epoch of 1970-01-01T00:00:00Z"));
+                hasEntry("description",
+                        "Number of seconds from the epoch of 1970-01-01T00:00:00Z"));
     }
 
     private Matcher<Map<? extends String, ? extends String>> epochSecondsRef() {
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java
index 23b3bd9..024856f 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/PetStoreAppTest.java
@@ -18,6 +18,7 @@ package org.eclipse.microprofile.openapi.tck;
 
 import static io.restassured.RestAssured.given;
 import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.comparesEqualToNumber;
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.itemOrSingleton;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasEntry;
@@ -60,14 +61,10 @@ public class PetStoreAppTest extends AppTestBase {
         vr.body("paths.'/store/order/{orderId}'.get.responses.'599'.schema", nullValue());
 
         // Numerical properties
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.maximum",
-                comparesEqualToNumber(101.0));
         vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.exclusiveMaximum",
-                equalTo(true));
-        vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.minimum",
-                comparesEqualToNumber(9));
+                comparesEqualToNumber(101.0));
         vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.exclusiveMinimum",
-                equalTo(true));
+                comparesEqualToNumber(9));
         vr.body("paths.'/pet/{petId}'.get.parameters.find{ it.name == 'petId' }.schema.multipleOf",
                 comparesEqualToNumber(10));
 
@@ -218,7 +215,7 @@ public class PetStoreAppTest extends AppTestBase {
         vr.body(schemaObject,
                 allOf(aMapWithSize(3),
                         hasEntry(equalTo("required"), notNullValue()),
-                        hasEntry(equalTo("type"), equalTo("object")),
+                        hasEntry(equalTo("type"), itemOrSingleton("object")),
                         hasEntry(equalTo("properties"), notNullValue())));
     }
 
@@ -241,7 +238,7 @@ public class PetStoreAppTest extends AppTestBase {
         vr.body(schemaObject,
                 allOf(aMapWithSize(3),
                         hasEntry(equalTo("required"), notNullValue()),
-                        hasEntry(equalTo("type"), equalTo("object")),
+                        hasEntry(equalTo("type"), itemOrSingleton("object")),
                         hasEntry(equalTo("properties"), notNullValue())));
     }
 
@@ -265,7 +262,7 @@ public class PetStoreAppTest extends AppTestBase {
 
         vr.body(arraySchemaObject,
                 allOf(aMapWithSize(2),
-                        hasEntry(equalTo("type"), equalTo("array")),
+                        hasEntry(equalTo("type"), itemOrSingleton("array")),
                         hasEntry(equalTo("items"), notNullValue())));
 
         String schemaObject = dereference(vr, arraySchemaObject + ".items");
@@ -273,7 +270,7 @@ public class PetStoreAppTest extends AppTestBase {
         vr.body(schemaObject,
                 allOf(aMapWithSize(3),
                         hasEntry(equalTo("required"), notNullValue()),
-                        hasEntry(equalTo("type"), equalTo("object")),
+                        hasEntry(equalTo("type"), itemOrSingleton("object")),
                         hasEntry(equalTo("properties"), notNullValue())));
     }
 
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/StaticDocumentCustomDialectTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/StaticDocumentCustomDialectTest.java
new file mode 100644
index 0000000..e4ec2f4
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/StaticDocumentCustomDialectTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2024 Contributors to the Eclipse Foundation
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.microprofile.openapi.tck;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.startsWith;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.testng.annotations.Test;
+
+import io.restassured.response.ValidatableResponse;
+
+/**
+ * Test that a static document can contain a schema with a custom dialect
+ */
+public class StaticDocumentCustomDialectTest extends AppTestBase {
+
+    @Deployment(name = "customdialect", testable = false)
+    public static WebArchive createDeployment() {
+        return ShrinkWrap.create(WebArchive.class, "customdialect.war")
+                .addAsManifestResource("customDialect.yaml", "openapi.yaml");
+    }
+
+    @Test(dataProvider = "formatProvider")
+    public void testStaticDocumentCustomDialect(String type) {
+        ValidatableResponse vr = callEndpoint(type);
+
+        vr.body("openapi", startsWith("3.1."));
+
+        final String schemaPath = "paths.'/test'.get.responses.'200'.content.'application/json'.schema";
+        vr.body(schemaPath + ".$schema", equalTo("http://example.com/custom"));
+        vr.body(schemaPath + ".foo", equalTo("bar"));
+        vr.body(schemaPath + ".baz", equalTo("qux"));
+    }
+
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/beanvalidation/BeanValidationTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/beanvalidation/BeanValidationTest.java
index f94d45a..396f394 100644
--- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/beanvalidation/BeanValidationTest.java
+++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/beanvalidation/BeanValidationTest.java
@@ -16,8 +16,10 @@
 package org.eclipse.microprofile.openapi.tck.beanvalidation;
 
 import static org.eclipse.microprofile.openapi.tck.Groups.BEAN_VALIDATION;
+import static org.eclipse.microprofile.openapi.tck.utils.TCKMatchers.itemOrSingleton;
 import static org.hamcrest.Matchers.hasEntry;
 import static org.hamcrest.Matchers.hasKey;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 
 import org.eclipse.microprofile.openapi.apps.beanvalidation.BeanValidationApp;
@@ -42,21 +44,21 @@ public class BeanValidationTest extends AppTestBase {
     public void notEmptyStringTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "notEmptyString", hasEntry("minLength", 1));
-        assertProperty(vr, "notEmptyString", hasEntry("type", "string"));
+        assertProperty(vr, "notEmptyString", hasEntry(is("type"), itemOrSingleton("string")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void notEmptyListTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "notEmptyList", hasEntry("minItems", 1));
-        assertProperty(vr, "notEmptyList", hasEntry("type", "array"));
+        assertProperty(vr, "notEmptyList", hasEntry(is("type"), itemOrSingleton("array")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void notEmptyMapTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "notEmptyMap", hasEntry("minProperties", 1));
-        assertProperty(vr, "notEmptyMap", hasEntry("type", "object"));
+        assertProperty(vr, "notEmptyMap", hasEntry(is("type"), itemOrSingleton("object")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
@@ -70,7 +72,7 @@ public class BeanValidationTest extends AppTestBase {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "sizedString", hasEntry("minLength", 2));
         assertProperty(vr, "sizedString", hasEntry("maxLength", 7));
-        assertProperty(vr, "sizedString", hasEntry("type", "string"));
+        assertProperty(vr, "sizedString", hasEntry(is("type"), itemOrSingleton("string")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
@@ -78,7 +80,7 @@ public class BeanValidationTest extends AppTestBase {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "sizedList", hasEntry("minItems", 1));
         assertProperty(vr, "sizedList", hasEntry("maxItems", 10));
-        assertProperty(vr, "sizedList", hasEntry("type", "array"));
+        assertProperty(vr, "sizedList", hasEntry(is("type"), itemOrSingleton("array")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
@@ -86,37 +88,39 @@ public class BeanValidationTest extends AppTestBase {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "sizedMap", hasEntry("minProperties", 3));
         assertProperty(vr, "sizedMap", hasEntry("maxProperties", 5));
-        assertProperty(vr, "sizedMap", hasEntry("type", "object"));
+        assertProperty(vr, "sizedMap", hasEntry(is("type"), itemOrSingleton("object")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void maxDecimalInclusiveTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "maxDecimalInclusive", hasEntry("maximum", 1.5f));
-        assertProperty(vr, "maxDecimalInclusive", hasEntry("type", "number"));
+        assertProperty(vr, "maxDecimalInclusive", not(hasKey("exclusiveMaximum")));
+        assertProperty(vr, "maxDecimalInclusive", hasEntry(is("type"), itemOrSingleton("number")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void maxDecimalExclusiveTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
-        assertProperty(vr, "maxDecimalExclusive", hasEntry("maximum", 1.5f));
-        assertProperty(vr, "maxDecimalExclusive", hasEntry("exclusiveMaximum", true));
-        assertProperty(vr, "maxDecimalExclusive", hasEntry("type", "number"));
+        assertProperty(vr, "maxDecimalExclusive", hasEntry("exclusiveMaximum", 1.5f));
+        assertProperty(vr, "maxDecimalExclusive", not(hasKey("maximum")));
+        assertProperty(vr, "maxDecimalExclusive", hasEntry(is("type"), itemOrSingleton("number")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void minDecimalInclusiveTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "minDecimalInclusive", hasEntry("minimum", 3.25f));
-        assertProperty(vr, "minDecimalInclusive", hasEntry("type", "number"));
+        assertProperty(vr, "minDecimalInclusive", not(hasKey("exclusiveMinimum")));
+        assertProperty(vr, "minDecimalInclusive", hasEntry(is("type"), itemOrSingleton("number")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void minDecimalExclusiveTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
-        assertProperty(vr, "minDecimalExclusive", hasEntry("minimum", 3.25f));
-        assertProperty(vr, "minDecimalExclusive", hasEntry("exclusiveMinimum", true));
-        assertProperty(vr, "minDecimalExclusive", hasEntry("type", "number"));
+        assertProperty(vr, "minDecimalExclusive", hasEntry("exclusiveMinimum", 3.25f));
+        assertProperty(vr, "minDecimalExclusive", not(hasKey("minimum")));
+        assertProperty(vr, "minDecimalExclusive", hasEntry(is("type"), itemOrSingleton("number")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
@@ -134,27 +138,29 @@ public class BeanValidationTest extends AppTestBase {
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void negativeIntTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
-        assertProperty(vr, "negativeInt", hasEntry("maximum", 0));
-        assertProperty(vr, "negativeInt", hasEntry("exclusiveMaximum", true));
+        assertProperty(vr, "negativeInt", hasEntry("exclusiveMaximum", 0));
+        assertProperty(vr, "negativeInt", not(hasKey("maximum")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void negativeOrZeroIntTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "negativeOrZeroInt", hasEntry("maximum", 0));
+        assertProperty(vr, "negativeOrZeroInt", not(hasKey("exclusiveMaximum")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void positiveIntTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
-        assertProperty(vr, "positiveInt", hasEntry("minimum", 0));
-        assertProperty(vr, "positiveInt", hasEntry("exclusiveMinimum", true));
+        assertProperty(vr, "positiveInt", hasEntry("exclusiveMinimum", 0));
+        assertProperty(vr, "positiveInt", not(hasKey("minimum")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
     public void positiveOrZeroIntTest(String format) {
         ValidatableResponse vr = callEndpoint(format);
         assertProperty(vr, "positiveOrZeroInt", hasEntry("minimum", 0));
+        assertProperty(vr, "positiveOrZeroInt", not(hasKey("exclusiveMinimum")));
     }
 
     @Test(dataProvider = "formatProvider", groups = BEAN_VALIDATION)
@@ -180,7 +186,7 @@ public class BeanValidationTest extends AppTestBase {
         ValidatableResponse vr = callEndpoint(format);
         String schemaPath = dereference(vr, "paths.'/parameter/{test}'.post.parameters[0]", "schema");
         vr.body(schemaPath, hasEntry("maxLength", 6));
-        vr.body(schemaPath, hasEntry("type", "string"));
+        vr.body(schemaPath, hasEntry(is("type"), itemOrSingleton("string")));
     }
 
     /**