Repository: victools/jsonschema-generator
Pull request #10 (2019-08-31 21:20:24)

Additional Property Support: "required", "default", "pattern"
This includes changes proposed by @FlorianDidier in PR #5 (adding support for "required" and "default"). Thanks again!

I've made slight adjustments to not limit this to `String` default values but also support numeric and boolean ones or even any kind of object (the latter is only intended for edge cases and being dependent on a respective configuration of the used `ObjectMapper` to still produce a valid JSON Schema).

Additionally, issue #9 was logged at pretty much the same time with a similar request: supporting the "pattern" property. This has been included here now as well.


Patch:
diff --git a/.gitignore b/.gitignore
index 2e003c4..18227ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,36 @@
-# compiled project
-target
+######################
+# Eclipse
+######################
+.project
+.metadata
+bin
+tmp
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings
+.loadpath
+
+######################
+# Intellij
+######################
+.idea
+*.iml
+*.iws
+*.ipr
+*.ids
+*.orig
 
-# IDE configuration (NetBeans)
+######################
+# NetBeans
+######################
 nb-configuration.xml
 nbactions.xml
+
+######################
+# Build Directories
+######################
+target
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d74ebe..570410e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ## [Unreleased]
 ### Added
 - In `SchemaGenerator.generateSchema(Type)` also allow passing type parameters via `SchemaGenerator.generateSchema(Type, Type...)`.
+- Support for "required" property via `SchemaGeneratorConfigPart.withRequiredCheck()` (PR #5).
+- Support for "default" property via `SchemaGeneratorConfigPart.withDefaultResolver()` (PR #5).
+- Support for "pattern" property via `SchemaGeneratorConfigPart.withStringPatternResolver()` (Issue #9).
 
 ## [3.1.0] – 2019-06-18
 ### Changed
diff --git a/pom.xml b/pom.xml
index 277395c..d451a75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
 
     <groupId>com.github.victools</groupId>
     <artifactId>jsonschema-generator</artifactId>
-    <version>3.1.0</version>
+    <version>3.2.0-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <licenses>
@@ -27,7 +27,7 @@
         <connection>scm:git:ssh://github.com/victools/jsonschema-generator.git</connection>
         <developerConnection>scm:git:ssh://git@github.com/victools/jsonschema-generator.git</developerConnection>
         <url>https://github.com/victools/jsonschema-generator</url>
-        <tag>v3.1.0</tag>
+        <tag>HEAD</tag>
     </scm>
 
     <organization>
@@ -41,7 +41,17 @@
             <name>Carsten Wickner</name>
         </developer>
     </developers>
-    
+
+    <contributors>
+        <contributor>
+            <name>Florian Didier</name>
+            <url>https://github.com/imIfOu</url>
+            <roles>
+                <role>Provided PR #5 (support for "required" and "default" properties)</role>
+            </roles>
+        </contributor>
+    </contributors>
+
     <distributionManagement>
         <snapshotRepository>
             <id>ossrh</id>
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java
index 57c9ae8..4669e30 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaConstants.java
@@ -39,6 +39,7 @@ public interface SchemaConstants {
 
     String TAG_PROPERTIES = "properties";
     String TAG_ITEMS = "items";
+    String TAG_REQUIRED = "required";
 
     String TAG_ALLOF = "allOf";
     String TAG_ANYOF = "anyOf";
@@ -48,10 +49,12 @@ public interface SchemaConstants {
     String TAG_DESCRIPTION = "description";
     String TAG_CONST = "const";
     String TAG_ENUM = "enum";
+    String TAG_DEFAULT = "default";
 
     String TAG_LENGTH_MIN = "minLength";
     String TAG_LENGTH_MAX = "maxLength";
     String TAG_FORMAT = "format";
+    String TAG_PATTERN = "pattern";
 
     String TAG_MINIMUM = "minimum";
     String TAG_MINIMUM_EXCLUSIVE = "exclusiveMinimum";
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
index 1470af8..366fda7 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGenerator.java
@@ -29,11 +29,11 @@ import com.fasterxml.jackson.databind.node.TextNode;
 import com.github.victools.jsonschema.generator.impl.AttributeCollector;
 import com.github.victools.jsonschema.generator.impl.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -191,14 +191,21 @@ public class SchemaGenerator {
 
                 final Map<String, JsonNode> targetFields = new TreeMap<>();
                 final Map<String, JsonNode> targetMethods = new TreeMap<>();
+                final Set<String> requiredProperties = new HashSet<>();
 
-                this.collectObjectProperties(targetType, targetFields, targetMethods, generationContext);
+                this.collectObjectProperties(targetType, targetFields, targetMethods, requiredProperties, generationContext);
 
                 if (!targetFields.isEmpty() || !targetMethods.isEmpty()) {
                     ObjectNode propertiesNode = this.config.createObjectNode();
                     propertiesNode.setAll(targetFields);
                     propertiesNode.setAll(targetMethods);
                     definition.set(SchemaConstants.TAG_PROPERTIES, propertiesNode);
+
+                    if (!requiredProperties.isEmpty()) {
+                        ArrayNode requiredNode = this.config.createArrayNode();
+                        requiredProperties.forEach(requiredNode::add);
+                        definition.set(SchemaConstants.TAG_REQUIRED, requiredNode);
+                    }
                 }
             } else {
                 logger.debug("applying configured custom definition for {}", targetType);
@@ -214,15 +221,16 @@ public class SchemaGenerator {
      * @param targetType the type for which to collect fields and methods
      * @param targetFields map of named JSON schema nodes representing individual fields
      * @param targetMethods map of named JSON schema nodes representing individual methods
+     * @param requiredProperties set of properties value required
      * @param generationContext context to add type definitions and their references to (to be resolved at the end of the schema generation)
      */
     private void collectObjectProperties(ResolvedType targetType, Map<String, JsonNode> targetFields, Map<String, JsonNode> targetMethods,
-            SchemaGenerationContext generationContext) {
+            Set<String> requiredProperties, SchemaGenerationContext generationContext) {
         logger.debug("collecting non-static fields and methods from {}", targetType);
         final ResolvedTypeWithMembers targetTypeWithMembers = generationContext.getTypeContext().resolveWithMembers(targetType);
         // member fields and methods are being collected from the targeted type as well as its super types
-        this.populateFields(targetTypeWithMembers, ResolvedTypeWithMembers::getMemberFields, targetFields, generationContext);
-        this.populateMethods(targetTypeWithMembers, ResolvedTypeWithMembers::getMemberMethods, targetMethods, generationContext);
+        this.populateFields(targetTypeWithMembers, ResolvedTypeWithMembers::getMemberFields, targetFields, requiredProperties, generationContext);
+        this.populateMethods(targetTypeWithMembers, ResolvedTypeWithMembers::getMemberMethods, targetMethods, requiredProperties, generationContext);
 
         final boolean includeStaticFields = this.config.shouldIncludeStaticFields();
         final boolean includeStaticMethods = this.config.shouldIncludeStaticMethods();
@@ -244,10 +252,12 @@ public class SchemaGenerator {
                     hierarchyTypeMembers = generationContext.getTypeContext().resolveWithMembers(hierachyType);
                 }
                 if (includeStaticFields) {
-                    this.populateFields(hierarchyTypeMembers, ResolvedTypeWithMembers::getStaticFields, targetFields, generationContext);
+                    this.populateFields(hierarchyTypeMembers, ResolvedTypeWithMembers::getStaticFields, targetFields,
+                            requiredProperties, generationContext);
                 }
                 if (includeStaticMethods) {
-                    this.populateMethods(hierarchyTypeMembers, ResolvedTypeWithMembers::getStaticMethods, targetMethods, generationContext);
+                    this.populateMethods(hierarchyTypeMembers, ResolvedTypeWithMembers::getStaticMethods, targetMethods,
+                            requiredProperties, generationContext);
                 }
             }
         }
@@ -259,14 +269,15 @@ public class SchemaGenerator {
      * @param declaringTypeMembers the type declaring the fields to populate
      * @param fieldLookup retrieval function for getter targeted fields from {@code declaringTypeMembers}
      * @param collectedFields property nodes in the JSON schema to which the field sub schemas should be added
+     * @param requiredProperties set of properties value required
      * @param generationContext context to add type definitions and their references to (to be resolved at the end of the schema generation)
      */
     private void populateFields(ResolvedTypeWithMembers declaringTypeMembers, Function<ResolvedTypeWithMembers, ResolvedField[]> fieldLookup,
-            Map<String, JsonNode> collectedFields, SchemaGenerationContext generationContext) {
+            Map<String, JsonNode> collectedFields, Set<String> requiredProperties, SchemaGenerationContext generationContext) {
         Stream.of(fieldLookup.apply(declaringTypeMembers))
                 .map(declaredField -> generationContext.getTypeContext().createFieldScope(declaredField, declaringTypeMembers))
                 .filter(fieldScope -> !this.config.shouldIgnore(fieldScope))
-                .forEach(fieldScope -> this.populateField(fieldScope, collectedFields, generationContext));
+                .forEach(fieldScope -> this.populateField(fieldScope, collectedFields, requiredProperties, generationContext));
     }
 
     /**
@@ -275,14 +286,15 @@ public class SchemaGenerator {
      * @param declaringTypeMembers the type declaring the methods to populate
      * @param methodLookup retrieval function for getter targeted methods from {@code declaringTypeMembers}
      * @param collectedMethods property nodes in the JSON schema to which the method sub schemas should be added
+     * @param requiredProperties set of properties value required
      * @param generationContext context to add type definitions and their references to (to be resolved at the end of the schema generation)
      */
     private void populateMethods(ResolvedTypeWithMembers declaringTypeMembers, Function<ResolvedTypeWithMembers, ResolvedMethod[]> methodLookup,
-            Map<String, JsonNode> collectedMethods, SchemaGenerationContext generationContext) {
+            Map<String, JsonNode> collectedMethods, Set<String> requiredProperties, SchemaGenerationContext generationContext) {
         Stream.of(methodLookup.apply(declaringTypeMembers))
                 .map(declaredMethod -> generationContext.getTypeContext().createMethodScope(declaredMethod, declaringTypeMembers))
                 .filter(methodScope -> !this.config.shouldIgnore(methodScope))
-                .forEach(methodScope -> this.populateMethod(methodScope, collectedMethods, generationContext));
+                .forEach(methodScope -> this.populateMethod(methodScope, collectedMethods, requiredProperties, generationContext));
     }
 
     /**
@@ -290,12 +302,17 @@ public class SchemaGenerator {
      *
      * @param field declared field that should be added to the specified node
      * @param collectedFields node in the JSON schema to which the field's sub schema should be added as property
+     * @param requiredProperties set of properties value required
      * @param generationContext context to add type definitions and their references to (to be resolved at the end of the schema generation)
      */
-    private void populateField(FieldScope field, Map<String, JsonNode> collectedFields, SchemaGenerationContext generationContext) {
+    private void populateField(FieldScope field, Map<String, JsonNode> collectedFields, Set<String> requiredProperties,
+            SchemaGenerationContext generationContext) {
         String propertyNameOverride = this.config.resolvePropertyNameOverride(field);
         FieldScope fieldWithOverride = propertyNameOverride == null ? field : field.withOverriddenName(propertyNameOverride);
         String propertyName = fieldWithOverride.getSchemaPropertyName();
+        if (this.config.isRequired(field)) {
+            requiredProperties.add(propertyName);
+        }
         if (collectedFields.containsKey(propertyName)) {
             logger.debug("ignoring overridden {}.{}", fieldWithOverride.getDeclaringType(), fieldWithOverride.getDeclaredName());
             return;
@@ -318,12 +335,17 @@ public class SchemaGenerator {
      *
      * @param method declared method that should be added to the specified node
      * @param collectedMethods node in the JSON schema to which the method's (and its return value's) sub schema should be added as property
+     * @param requiredProperties set of properties value required
      * @param generationContext context to add type definitions and their references to (to be resolved at the end of the schema generation)
      */
-    private void populateMethod(MethodScope method, Map<String, JsonNode> collectedMethods, SchemaGenerationContext generationContext) {
+    private void populateMethod(MethodScope method, Map<String, JsonNode> collectedMethods, Set<String> requiredProperties,
+            SchemaGenerationContext generationContext) {
         String propertyNameOverride = this.config.resolvePropertyNameOverride(method);
         MethodScope methodWithOverride = propertyNameOverride == null ? method : method.withOverriddenName(propertyNameOverride);
         String propertyName = methodWithOverride.getSchemaPropertyName();
+        if (this.config.isRequired(method)) {
+            requiredProperties.add(propertyName);
+        }
         if (collectedMethods.containsKey(propertyName)) {
             logger.debug("ignoring overridden {}.{}", methodWithOverride.getDeclaringType(), methodWithOverride.getDeclaredName());
             return;
@@ -353,9 +375,9 @@ public class SchemaGenerator {
      * @param targetNode node in the JSON schema that should represent the associated javaType and include the separately collected attributes
      * @param isNullable whether the field/method's return value the javaType refers to is allowed to be null in the declaringType
      * @param collectedAttributes separately collected attribute for the field/method in their respective declaring type
-     * @param generationContext context to add type definitions and their references to (to be resolved at the end of the schema generation)
-     * @see #populateField(Field, Type, ObjectNode, Map, SchemaGenerationContext)
-     * @see #populateMethod(Method, Type, ObjectNode, Map, SchemaGenerationContext)
+     * @param generationContext   context to add type definitions and their references to (to be resolved at the end of the schema generation)
+     * @see #populateField(FieldScope, Map, Set, SchemaGenerationContext)
+     * @see #populateMethod(MethodScope, Map, Set, SchemaGenerationContext)
      */
     private void populateSchema(ResolvedType javaType, ObjectNode targetNode, boolean isNullable,
             ObjectNode collectedAttributes, SchemaGenerationContext generationContext) {
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
index 6dbb043..df1a770 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfig.java
@@ -128,6 +128,22 @@ public interface SchemaGeneratorConfig {
      */
     boolean isNullable(MethodScope method);
 
+    /**
+     * Check whether a field/property value is required.
+     *
+     * @param field object's field/property to check
+     * @return whether the field/property value should be required
+     */
+    boolean isRequired(FieldScope field);
+
+    /**
+     * Check whether a method value is required.
+     *
+     * @param method method to check
+     * @return whether the method value should be required
+     */
+    boolean isRequired(MethodScope method);
+
     /**
      * Check whether a field/property should be ignored.
      *
@@ -208,6 +224,22 @@ public interface SchemaGeneratorConfig {
      */
     String resolveDescription(MethodScope method);
 
+    /**
+     * Determine the "default" value of a method's return value.
+     *
+     * @param method method for whose return value to determine "default" value for
+     * @return "default" in a JSON Schema (may be null)
+     */
+    Object resolveDefault(MethodScope method);
+
+    /**
+     * Determine the "default" value of an object's field/property.
+     *
+     * @param field object's field/property to determine "default" value for
+     * @return "default" in a JSON Schema (may be null)
+     */
+    Object resolveDefault(FieldScope field);
+
     /**
      * Determine the "enum"/"const" of an object's field/property.
      *
@@ -272,6 +304,22 @@ public interface SchemaGeneratorConfig {
      */
     String resolveStringFormat(MethodScope method);
 
+    /**
+     * Determine the "pattern" of an object's field/property.
+     *
+     * @param field object's field/property to determine "pattern" value for
+     * @return "pattern" in a JSON Schema (may be null)
+     */
+    String resolveStringPattern(FieldScope field);
+
+    /**
+     * Determine the "pattern" of a method's return value.
+     *
+     * @param method method for whose return value to determine "pattern" value for
+     * @return "pattern" in a JSON Schema (may be null)
+     */
+    String resolveStringPattern(MethodScope method);
+
     /**
      * Determine the "minimum" of an object's field/property.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java
index b719da7..7d4d8fe 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPart.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
@@ -54,9 +55,11 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
     private final List<InstanceAttributeOverride<M>> instanceAttributeOverrides = new ArrayList<>();
 
     /*
-     * Customising options for properties in a schema with "type": object; either skipping them completely or also allowing for "type": "null".
+     * Customising options for properties in a schema with "type": object;
+     * either skipping them completely, marking them as required or also allowing for "type": "null".
      */
     private final List<Predicate<M>> ignoreChecks = new ArrayList<>();
+    private final List<Predicate<M>> requiredChecks = new ArrayList<>();
     private final List<ConfigFunction<M, Boolean>> nullableChecks = new ArrayList<>();
 
     /*
@@ -70,7 +73,9 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
      */
     private final List<ConfigFunction<M, String>> titleResolvers = new ArrayList<>();
     private final List<ConfigFunction<M, String>> descriptionResolvers = new ArrayList<>();
+    private final List<ConfigFunction<M, Object>> defaultResolvers = new ArrayList<>();
     private final List<ConfigFunction<M, Collection<?>>> enumResolvers = new ArrayList<>();
+    private final List<ConfigFunction<M, Map<String, String>>> metadatas = new ArrayList<>();
 
     /*
      * Validation fields relating to a schema with "type": "string".
@@ -78,6 +83,7 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
     private final List<ConfigFunction<M, Integer>> stringMinLengthResolvers = new ArrayList<>();
     private final List<ConfigFunction<M, Integer>> stringMaxLengthResolvers = new ArrayList<>();
     private final List<ConfigFunction<M, String>> stringFormatResolvers = new ArrayList<>();
+    private final List<ConfigFunction<M, String>> stringPatternResolvers = new ArrayList<>();
 
     /*
      * Validation fields relating to a schema with "type": "integer" or "number".
@@ -136,6 +142,27 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
         return this.ignoreChecks.stream().anyMatch(check -> check.test(member));
     }
 
+    /**
+     * Setter for required check.
+     *
+     * @param check how to determine whether a given reference should have required value
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorConfigPart<M> withRequiredCheck(Predicate<M> check) {
+        this.requiredChecks.add(check);
+        return this;
+    }
+
+    /**
+     * Determine whether a given member should be indicated as being required in its declaring type.
+     *
+     * @param member member to check
+     * @return whether the member is required (defaults to false)
+     */
+    public boolean isRequired(M member) {
+        return this.requiredChecks.stream().anyMatch(check -> check.test(member));
+    }
+
     /**
      * Setter for nullable check.
      *
@@ -247,6 +274,27 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
         return getFirstDefinedValue(this.descriptionResolvers, member);
     }
 
+    /**
+     * Setter for "default" resolver.
+     *
+     * @param resolver how to determine the "default" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorConfigPart<M> withDefaultResolver(ConfigFunction<M, Object> resolver) {
+        this.defaultResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "default" of a given member.
+     *
+     * @param member member to determine "default" value for
+     * @return "default" in a JSON Schema (may be null)
+     */
+    public Object resolveDefault(M member) {
+        return getFirstDefinedValue(this.defaultResolvers, member);
+    }
+
     /**
      * Setter for "enum"/"const" resolver.
      *
@@ -331,6 +379,27 @@ public class SchemaGeneratorConfigPart<M extends MemberScope<?, ?>> {
         return getFirstDefinedValue(this.stringFormatResolvers, member);
     }
 
+    /**
+     * Setter for "format" resolver.
+     *
+     * @param resolver how to determine the "format" of a JSON Schema
+     * @return this config part (for chaining)
+     */
+    public SchemaGeneratorConfigPart<M> withStringPatternResolver(ConfigFunction<M, String> resolver) {
+        this.stringPatternResolvers.add(resolver);
+        return this;
+    }
+
+    /**
+     * Determine the "format" of a given member.
+     *
+     * @param member member to determine "format" value for
+     * @return "format" in a JSON Schema (may be null)
+     */
+    public String resolveStringPattern(M member) {
+        return getFirstDefinedValue(this.stringPatternResolvers, member);
+    }
+
     /**
      * Setter for "minimum" resolver.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java b/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
index 395ba1c..21bd944 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/AttributeCollector.java
@@ -20,11 +20,12 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.github.victools.jsonschema.generator.SchemaConstants;
-import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.MethodScope;
+import com.github.victools.jsonschema.generator.SchemaConstants;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -61,10 +62,12 @@ public class AttributeCollector {
         AttributeCollector collector = new AttributeCollector(config.getObjectMapper());
         collector.setTitle(node, config.resolveTitle(field));
         collector.setDescription(node, config.resolveDescription(field));
+        collector.setDefault(node, config.resolveDefault(field));
         collector.setEnum(node, config.resolveEnum(field));
         collector.setStringMinLength(node, config.resolveStringMinLength(field));
         collector.setStringMaxLength(node, config.resolveStringMaxLength(field));
         collector.setStringFormat(node, config.resolveStringFormat(field));
+        collector.setStringPattern(node, config.resolveStringPattern(field));
         collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimum(field));
         collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimum(field));
         collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximum(field));
@@ -90,10 +93,12 @@ public class AttributeCollector {
         AttributeCollector collector = new AttributeCollector(config.getObjectMapper());
         collector.setTitle(node, config.resolveTitle(method));
         collector.setDescription(node, config.resolveDescription(method));
+        collector.setDefault(node, config.resolveDefault(method));
         collector.setEnum(node, config.resolveEnum(method));
         collector.setStringMinLength(node, config.resolveStringMinLength(method));
         collector.setStringMaxLength(node, config.resolveStringMaxLength(method));
         collector.setStringFormat(node, config.resolveStringFormat(method));
+        collector.setStringPattern(node, config.resolveStringPattern(method));
         collector.setNumberInclusiveMinimum(node, config.resolveNumberInclusiveMinimum(method));
         collector.setNumberExclusiveMinimum(node, config.resolveNumberExclusiveMinimum(method));
         collector.setNumberInclusiveMaximum(node, config.resolveNumberInclusiveMaximum(method));
@@ -135,6 +140,38 @@ public class AttributeCollector {
         return this;
     }
 
+    /**
+     * Setter for "{@value SchemaConstants#TAG_DEFAULT}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param defaultValue attribute value to set
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setDefault(ObjectNode node, Object defaultValue) {
+        if (defaultValue != null) {
+            // need to specifically add simple/primitive values by type
+            if (defaultValue instanceof String) {
+                node.put(SchemaConstants.TAG_DEFAULT, (String) defaultValue);
+            } else if (defaultValue instanceof BigDecimal) {
+                node.put(SchemaConstants.TAG_DEFAULT, (BigDecimal) defaultValue);
+            } else if (defaultValue instanceof BigInteger) {
+                node.put(SchemaConstants.TAG_DEFAULT, (BigInteger) defaultValue);
+            } else if (defaultValue instanceof Boolean) {
+                node.put(SchemaConstants.TAG_DEFAULT, (Boolean) defaultValue);
+            } else if (defaultValue instanceof Double) {
+                node.put(SchemaConstants.TAG_DEFAULT, (Double) defaultValue);
+            } else if (defaultValue instanceof Float) {
+                node.put(SchemaConstants.TAG_DEFAULT, (Float) defaultValue);
+            } else if (defaultValue instanceof Integer) {
+                node.put(SchemaConstants.TAG_DEFAULT, (Integer) defaultValue);
+            } else {
+                // everything else is simply forwarded as-is to the JSON Schema, it's up to the configurator to ensure the value's correctness
+                node.putPOJO(SchemaConstants.TAG_DEFAULT, defaultValue);
+            }
+        }
+        return this;
+    }
+
     /**
      * Setter for "{@value SchemaConstants#TAG_CONST}"/"{@value SchemaConstants#TAG_ENUM}" attribute.
      *
@@ -234,6 +271,20 @@ public class AttributeCollector {
         return this;
     }
 
+    /**
+     * Setter for "{@value SchemaConstants#TAG_PATTERN}" attribute.
+     *
+     * @param node schema node to set attribute on
+     * @param pattern attribute value to set
+     * @return this instance (for chaining)
+     */
+    public AttributeCollector setStringPattern(ObjectNode node, String pattern) {
+        if (pattern != null) {
+            node.put(SchemaConstants.TAG_PATTERN, pattern);
+        }
+        return this;
+    }
+
     /**
      * Setter for "{@value SchemaConstants#TAG_MINIMUM}" attribute.
      *
diff --git a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
index 271fef2..fc6c40e 100644
--- a/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
+++ b/src/main/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImpl.java
@@ -166,6 +166,16 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.shouldIgnore(method);
     }
 
+    @Override
+    public boolean isRequired(FieldScope field) {
+        return this.fieldConfigPart.isRequired(field);
+    }
+
+    @Override
+    public boolean isRequired(MethodScope method) {
+        return this.methodConfigPart.isRequired(method);
+    }
+
     @Override
     public ResolvedType resolveTargetTypeOverride(FieldScope field) {
         return this.fieldConfigPart.resolveTargetTypeOverride(field);
@@ -206,6 +216,16 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveDescription(method);
     }
 
+    @Override
+    public Object resolveDefault(MethodScope method) {
+        return this.methodConfigPart.resolveDefault(method);
+    }
+
+    @Override
+    public Object resolveDefault(FieldScope field) {
+        return this.fieldConfigPart.resolveDefault(field);
+    }
+
     @Override
     public Collection<?> resolveEnum(FieldScope field) {
         return this.fieldConfigPart.resolveEnum(field);
@@ -246,6 +266,16 @@ public class SchemaGeneratorConfigImpl implements SchemaGeneratorConfig {
         return this.methodConfigPart.resolveStringFormat(method);
     }
 
+    @Override
+    public String resolveStringPattern(FieldScope field) {
+        return this.fieldConfigPart.resolveStringPattern(field);
+    }
+
+    @Override
+    public String resolveStringPattern(MethodScope method) {
+        return this.methodConfigPart.resolveStringPattern(method);
+    }
+
     @Override
     public BigDecimal resolveNumberInclusiveMinimum(FieldScope field) {
         return this.fieldConfigPart.resolveNumberInclusiveMinimum(field);


Test patch:
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPartTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPartTest.java
index 2ef7846..8b318ff 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPartTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorConfigPartTest.java
@@ -69,6 +69,20 @@ public class SchemaGeneratorConfigPartTest {
         Assert.assertFalse(this.instance.shouldIgnore(this.field3));
     }
 
+    @Test
+    public void testRequiredCheck() {
+        Assert.assertFalse(this.instance.isRequired(this.field1));
+
+        Assert.assertSame(this.instance, this.instance.withRequiredCheck(member -> member == this.field1));
+        Assert.assertTrue(this.instance.isRequired(this.field1));
+        Assert.assertFalse(this.instance.isRequired(this.field2));
+
+        Assert.assertSame(this.instance, this.instance.withRequiredCheck(member -> member == this.field2));
+        Assert.assertTrue(this.instance.isRequired(this.field1));
+        Assert.assertTrue(this.instance.isRequired(this.field2));
+        Assert.assertFalse(this.instance.isRequired(this.field3));
+    }
+
     @Test
     public void testNullableCheck() {
         Assert.assertNull(this.instance.isNullable(this.field1));
diff --git a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
index 58517a2..4a42973 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/SchemaGeneratorTest.java
@@ -80,18 +80,21 @@ public class SchemaGeneratorTest {
                 .withArrayMinItemsResolver(member -> member.isContainerType() ? 0 : null)
                 .withArrayMaxItemsResolver(member -> member.isContainerType() ? Integer.MAX_VALUE : null)
                 .withArrayUniqueItemsResolver(member -> member.isContainerType() ? false : null)
+                .withDefaultResolver(member -> member.getType().isInstanceOf(Number.class) ? 1 : null)
                 .withDescriptionResolver(member -> descriptionPrefix + member.getContext().getSimpleTypeDescription(member.getType()))
                 .withEnumResolver(member -> member.getType().isInstanceOf(Number.class) ? Arrays.asList(1, 2, 3, 4, 5) : null)
                 .withEnumResolver(member -> member.getType().isInstanceOf(String.class) ? Arrays.asList("constant string value") : null)
-                .withNullableCheck(member -> Boolean.TRUE)
+                .withNullableCheck(member -> !member.getName().startsWith("nested"))
                 .withNumberExclusiveMaximumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.TEN.add(BigDecimal.ONE) : null)
                 .withNumberExclusiveMinimumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.ZERO : null)
                 .withNumberInclusiveMaximumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.TEN : null)
                 .withNumberInclusiveMinimumResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.ONE : null)
                 .withNumberMultipleOfResolver(member -> member.getType().isInstanceOf(Number.class) ? BigDecimal.ONE : null)
+                .withRequiredCheck(member -> member.getName().startsWith("nested"))
                 .withStringFormatResolver(member -> member.getType().isInstanceOf(String.class) ? "date" : null)
                 .withStringMaxLengthResolver(member -> member.getType().isInstanceOf(String.class) ? 256 : null)
                 .withStringMinLengthResolver(member -> member.getType().isInstanceOf(String.class) ? 1 : null)
+                .withStringPatternResolver(member -> member.getType().isInstanceOf(String.class) ? "^.{1,256}$" : null)
                 .withTitleResolver(member -> member.getContext().getSimpleTypeDescription(member.getType()));
     }
 
diff --git a/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java b/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
index ba52721..03f55a5 100644
--- a/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
+++ b/src/test/java/com/github/victools/jsonschema/generator/impl/SchemaGeneratorConfigImplTest.java
@@ -16,19 +16,19 @@
 
 package com.github.victools.jsonschema.generator.impl;
 
-import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
 import com.fasterxml.classmate.ResolvedType;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.github.victools.jsonschema.generator.AbstractTypeAwareTest;
 import com.github.victools.jsonschema.generator.CustomDefinition;
 import com.github.victools.jsonschema.generator.CustomDefinitionProvider;
+import com.github.victools.jsonschema.generator.FieldScope;
 import com.github.victools.jsonschema.generator.InstanceAttributeOverride;
+import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.Option;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfigPart;
 import com.github.victools.jsonschema.generator.TypeAttributeOverride;
-import com.github.victools.jsonschema.generator.FieldScope;
-import com.github.victools.jsonschema.generator.MethodScope;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -182,6 +182,24 @@ public class SchemaGeneratorConfigImplTest extends AbstractTypeAwareTest {
         Assert.assertEquals(expectedResult, result);
     }
 
+    @Test
+    @Parameters({"true", "false"})
+    public void testIsRequiredField(boolean configuredAndExpectedResult) throws Exception {
+        FieldScope field = this.getTestClassField("field");
+        Mockito.when(this.fieldConfigPart.isRequired(field)).thenReturn(configuredAndExpectedResult);
+        boolean result = this.instance.isRequired(field);
+        Assert.assertEquals(configuredAndExpectedResult, result);
+    }
+
+    @Test
+    @Parameters({"true", "false"})
+    public void testIsRequiredMethod(boolean configuredAndExpectedResult) throws Exception {
+        MethodScope method = this.getTestClassMethod("getField");
+        Mockito.when(this.methodConfigPart.isRequired(method)).thenReturn(configuredAndExpectedResult);
+        boolean result = this.instance.isRequired(method);
+        Assert.assertEquals(configuredAndExpectedResult, result);
+    }
+
     @Test
     public void testGetTypeAttributeOverrides() {
         TypeAttributeOverride typeOverride = (typeNode, javaType, c) -> typeNode.put("$comment", javaType.getTypeName());
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass1-JAVA_OBJECT-attributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass1-JAVA_OBJECT-attributes.json
index 5c1e6f4..3bf7601 100644
--- a/src/test/resources/com/github/victools/jsonschema/generator/testclass1-JAVA_OBJECT-attributes.json
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass1-JAVA_OBJECT-attributes.json
@@ -19,7 +19,8 @@
             "const": "constant string value",
             "minLength": 1,
             "maxLength": 256,
-            "format": "date"
+            "format": "date",
+            "pattern": "^.{1,256}$"
         },
         "getPrimitiveValue()": {
             "type": ["integer", "null"],
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-attributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-attributes.json
index e6573b0..6e0fb2f 100644
--- a/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-attributes.json
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass1-PLAIN_JSON-attributes.json
@@ -15,12 +15,14 @@
             "const": "constant string value",
             "minLength": 1,
             "maxLength": 256,
-            "format": "date"
+            "format": "date",
+            "pattern": "^.{1,256}$"
         },
         "ignoredInternalValue": {
             "type": ["integer", "null"],
             "title": "Integer",
             "description": "looked-up from field: Integer",
+            "default": 1,
             "enum": [1, 2, 3, 4, 5],
             "minimum": 1,
             "exclusiveMinimum": 0,
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-attributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-attributes.json
index e1e7e2f..7c788ba 100644
--- a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-attributes.json
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-JAVA_OBJECT-attributes.json
@@ -15,7 +15,8 @@
                     "const": "constant string value",
                     "minLength": 1,
                     "maxLength": 256,
-                    "format": "date"
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "valueOf(String)": {
                     "allOf": [{
@@ -69,7 +70,8 @@
                     "const": "constant string value",
                     "minLength": 1,
                     "maxLength": 256,
-                    "format": "date"
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "getPrimitiveValue()": {
                     "type": ["integer", "null"],
@@ -96,6 +98,7 @@
                     "type": ["integer", "null"],
                     "title": "Long",
                     "description": "looked-up from method: Long",
+                    "default": 1,
                     "enum": [1, 2, 3, 4, 5],
                     "minimum": 1,
                     "exclusiveMinimum": 0,
@@ -121,7 +124,8 @@
                     "const": "constant string value",
                     "minLength": 1,
                     "maxLength": 256,
-                    "format": "date"
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 }
             }
         }
@@ -149,7 +153,8 @@
                                     "const": "constant string value",
                                     "minLength": 1,
                                     "maxLength": 256,
-                                    "format": "date"
+                                    "format": "date",
+                                    "pattern": "^.{1,256}$"
                                 },
                                 "isPresent()": {
                                     "type": ["boolean", "null"],
@@ -163,7 +168,8 @@
                                     "const": "constant string value",
                                     "minLength": 1,
                                     "maxLength": 256,
-                                    "format": "date"
+                                    "format": "date",
+                                    "pattern": "^.{1,256}$"
                                 }
                             }
                         },
diff --git a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-attributes.json b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-attributes.json
index 2e652ec..47ea4de 100644
--- a/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-attributes.json
+++ b/src/test/resources/com/github/victools/jsonschema/generator/testclass3-PLAIN_JSON-attributes.json
@@ -17,12 +17,14 @@
                     "const": "constant string value",
                     "minLength": 1,
                     "maxLength": 256,
-                    "format": "date"
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 },
                 "ignoredInternalValue": {
                     "type": ["integer", "null"],
                     "title": "Integer",
                     "description": "looked-up from field: Integer",
+                    "default": 1,
                     "enum": [1, 2, 3, 4, 5],
                     "minimum": 1,
                     "exclusiveMinimum": 0,
@@ -50,6 +52,7 @@
                     "type": ["integer", "null"],
                     "title": "Long",
                     "description": "looked-up from field: Long",
+                    "default": 1,
                     "enum": [1, 2, 3, 4, 5],
                     "minimum": 1,
                     "exclusiveMinimum": 0,
@@ -75,7 +78,8 @@
                     "const": "constant string value",
                     "minLength": 1,
                     "maxLength": 256,
-                    "format": "date"
+                    "format": "date",
+                    "pattern": "^.{1,256}$"
                 }
             }
         }
@@ -121,7 +125,8 @@
                             "const": "constant string value",
                             "minLength": 1,
                             "maxLength": 256,
-                            "format": "date"
+                            "format": "date",
+                            "pattern": "^.{1,256}$"
                         }
                     }
                 }, {
@@ -131,7 +136,7 @@
         },
         "nestedClass1Array": {
             "allOf": [{
-                    "type": ["object", "null"],
+                    "type": "object",
                     "properties": {
                         "genericArray": {
                             "type": ["array", "null"],
@@ -156,21 +161,18 @@
         },
         "nestedLong": {
             "allOf": [{
-                    "oneOf": [{
-                            "type": "null"
-                        }, {
-                            "$ref": "#/definitions/TestClass2<Long>"
-                        }]
+                    "$ref": "#/definitions/TestClass2<Long>"
                 }, {
                     "title": "TestClass2<Long>",
                     "description": "looked-up from field: TestClass2<Long>"
                 }]
         },
         "nestedLongList": {
-            "type": ["array", "null"],
+            "type": "array",
             "items": {
                 "$ref": "#/definitions/TestClass2<Long>"
             }
         }
-    }
+    },
+    "required": ["nestedLongList", "nestedLong", "nestedClass1Array"]
 }