Eliminate server from Path class
[netconf.git] / restconf / restconf-openapi / src / main / java / org / opendaylight / restconf / openapi / impl / DefinitionGenerator.java
index 383f274cf2063e19c8d235cba4f2fcde5092bcad..0e818dc2ad294d1e35962a5dfc5a23f54ce16c5c 100644 (file)
@@ -9,34 +9,30 @@ package org.opendaylight.restconf.openapi.impl;
 
 import static org.opendaylight.restconf.openapi.impl.BaseYangOpenApiGenerator.MODULE_NAME_SUFFIX;
 import static org.opendaylight.restconf.openapi.model.builder.OperationBuilder.COMPONENTS_PREFIX;
-import static org.opendaylight.restconf.openapi.model.builder.OperationBuilder.CONFIG;
-import static org.opendaylight.restconf.openapi.model.builder.OperationBuilder.NAME_KEY;
-import static org.opendaylight.restconf.openapi.model.builder.OperationBuilder.TOP;
-import static org.opendaylight.restconf.openapi.model.builder.OperationBuilder.XML_KEY;
-
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.fasterxml.jackson.databind.node.TextNode;
+
 import com.google.common.collect.Range;
 import com.google.common.collect.RangeSet;
 import dk.brics.automaton.RegExp;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.regex.Pattern;
+import org.opendaylight.restconf.openapi.model.Property;
 import org.opendaylight.restconf.openapi.model.Schema;
+import org.opendaylight.restconf.openapi.model.Xml;
 import org.opendaylight.yangtools.yang.common.Decimal64;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.XMLNamespace;
 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -44,6 +40,7 @@ import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
+import org.opendaylight.yangtools.yang.model.api.ElementCountConstraintAware;
 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
@@ -69,7 +66,6 @@ import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
@@ -86,25 +82,12 @@ import org.slf4j.LoggerFactory;
 /**
  * Generates JSON Schema for data defined in YANG. This class is not thread-safe.
  */
-public class DefinitionGenerator {
+public final class DefinitionGenerator {
 
     private static final Logger LOG = LoggerFactory.getLogger(DefinitionGenerator.class);
 
-    private static final String UNIQUE_ITEMS_KEY = "uniqueItems";
-    private static final String MAX_ITEMS = "maxItems";
-    private static final String MIN_ITEMS = "minItems";
-    private static final String MAX_LENGTH_KEY = "maxLength";
-    private static final String MIN_LENGTH_KEY = "minLength";
-    private static final String REF_KEY = "$ref";
-    private static final String ITEMS_KEY = "items";
     private static final String TYPE_KEY = "type";
-    private static final String DESCRIPTION_KEY = "description";
     private static final String ARRAY_TYPE = "array";
-    private static final String ENUM_KEY = "enum";
-    private static final String TITLE_KEY = "title";
-    private static final String DEFAULT_KEY = "default";
-    private static final String FORMAT_KEY = "format";
-    private static final String NAMESPACE_KEY = "namespace";
     public static final String INPUT = "input";
     public static final String INPUT_SUFFIX = "_input";
     public static final String OUTPUT = "output";
@@ -125,9 +108,8 @@ public class DefinitionGenerator {
             "\\\\D", "[^0-9]", "\\\\s", "[ \t\n\f\r]", "\\\\S", "[^ \t\n\f\r]",
             "\\\\w", "[a-zA-Z_0-9]", "\\\\W", "[^a-zA-Z_0-9]");
 
-    private Module topLevelModule;
-
-    public DefinitionGenerator() {
+    private DefinitionGenerator() {
+        // Hidden on purpose
     }
 
     /**
@@ -139,10 +121,9 @@ public class DefinitionGenerator {
      * @return {@link Map} containing data used for creating examples and definitions in OpenAPI documentation
      * @throws IOException if I/O operation fails
      */
-    public Map<String, Schema> convertToSchemas(final Module module, final EffectiveModelContext schemaContext,
+    private static Map<String, Schema> convertToSchemas(final Module module, final EffectiveModelContext schemaContext,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames,
             final boolean isForSingleModule) throws IOException {
-        topLevelModule = module;
 
         processIdentities(module, definitions, definitionNames, schemaContext);
         processContainersAndLists(module, definitions, definitionNames, schemaContext);
@@ -155,7 +136,7 @@ public class DefinitionGenerator {
         return definitions;
     }
 
-    public Map<String, Schema> convertToSchemas(final Module module, final EffectiveModelContext schemaContext,
+    public static Map<String, Schema> convertToSchemas(final Module module, final EffectiveModelContext schemaContext,
             final DefinitionNames definitionNames, final boolean isForSingleModule)
             throws IOException {
         final Map<String, Schema> definitions = new HashMap<>();
@@ -165,11 +146,10 @@ public class DefinitionGenerator {
         return convertToSchemas(module, schemaContext, definitions, definitionNames, isForSingleModule);
     }
 
-    private void processModule(final Module module, final Map<String, Schema> definitions,
+    private static void processModule(final Module module, final Map<String, Schema> definitions,
             final DefinitionNames definitionNames, final EffectiveModelContext schemaContext) {
-        final Schema definition = new Schema();
-        final ObjectNode properties = JsonNodeFactory.instance.objectNode();
-        final ArrayNode required = JsonNodeFactory.instance.arrayNode();
+        final Map<String, Property> properties = new HashMap<>();
+        final List<String> required = new ArrayList<>();
         final String moduleName = module.getName();
         final String definitionName = moduleName + MODULE_NAME_SUFFIX;
         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
@@ -178,52 +158,78 @@ public class DefinitionGenerator {
             final String localName = node.getQName().getLocalName();
             if (node.isConfiguration()) {
                 if (node instanceof ContainerSchemaNode || node instanceof ListSchemaNode) {
+                    if (isSchemaNodeMandatory(node)) {
+                        required.add(localName);
+                    }
                     for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
-                        final ObjectNode childNodeProperties = JsonNodeFactory.instance.objectNode();
+                        final Property.Builder childNodeProperty = new Property.Builder();
 
                         final String ref = COMPONENTS_PREFIX
-                                + moduleName + CONFIG
+                                + moduleName
                                 + "_" + localName
                                 + definitionNames.getDiscriminator(node);
 
                         if (node instanceof ListSchemaNode) {
-                            childNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
-                            final ObjectNode items = JsonNodeFactory.instance.objectNode();
-                            items.put(REF_KEY, ref);
-                            childNodeProperties.set(ITEMS_KEY, items);
-                            childNodeProperties.put(DESCRIPTION_KEY, childNode.getDescription().orElse(""));
-                            childNodeProperties.put(TITLE_KEY, localName + CONFIG);
+                            childNodeProperty.type(ARRAY_TYPE);
+                            final Property items = new Property.Builder().ref(ref).build();
+                            childNodeProperty.items(items);
+                            childNodeProperty.description(childNode.getDescription().orElse(""));
+                            childNodeProperty.title(localName);
                         } else {
                          /*
                             Description can't be added, because nothing allowed alongside $ref.
                             allOf is not an option, because ServiceNow can't parse it.
                           */
-                            childNodeProperties.put(REF_KEY, ref);
+                            childNodeProperty.ref(ref);
                         }
                         //add module name prefix to property name, when ServiceNow can process colons
-                        properties.set(localName, childNodeProperties);
+                        properties.put(localName, childNodeProperty.build());
                     }
                 } else if (node instanceof LeafSchemaNode) {
                     /*
                         Add module name prefix to property name, when ServiceNow can process colons(second parameter
                         of processLeafNode).
                      */
-                    processLeafNode((LeafSchemaNode) node, localName, properties, required, stack,
-                            definitions, definitionNames);
+                    final Property leafNode = processLeafNode((LeafSchemaNode) node, localName, required, stack,
+                            definitions, definitionNames, module.getNamespace(), module);
+                    properties.put(localName, leafNode);
                 }
             }
             stack.exit();
         }
-        definition.setTitle(definitionName);
-        definition.setType(OBJECT_TYPE);
-        definition.setProperties(properties);
-        definition.setDescription(module.getDescription().orElse(""));
-        setRequiredIfNotEmpty(definition, required);
+        final Schema.Builder definitionBuilder = new Schema.Builder()
+            .title(definitionName)
+            .type(OBJECT_TYPE)
+            .properties(properties)
+            .description(module.getDescription().orElse(""))
+            .required(required.size() > 0 ? required : null);
 
-        definitions.put(definitionName, definition);
+        definitions.put(definitionName, definitionBuilder.build());
     }
 
-    private void processContainersAndLists(final Module module, final Map<String, Schema> definitions,
+    private static boolean isSchemaNodeMandatory(final DataSchemaNode node) {
+        //    https://www.rfc-editor.org/rfc/rfc7950#page-14
+        //    mandatory node: A mandatory node is one of:
+        if (node instanceof ContainerSchemaNode containerNode) {
+            //  A container node without a "presence" statement and that has at least one mandatory node as a child.
+            if (containerNode.isPresenceContainer()) {
+                return false;
+            }
+            for (final DataSchemaNode childNode : containerNode.getChildNodes()) {
+                if (childNode instanceof MandatoryAware mandatoryAware && mandatoryAware.isMandatory()) {
+                    return true;
+                }
+            }
+        }
+        //  A list or leaf-list node with a "min-elements" statement with a value greater than zero.
+        return node instanceof ElementCountConstraintAware constraintAware
+                && constraintAware.getElementCountConstraint()
+                .map(ElementCountConstraint::getMinElements)
+                .orElse(0)
+                > 0;
+    }
+
+    private static void processContainersAndLists(final Module module, final Map<String, Schema> definitions,
             final DefinitionNames definitionNames, final EffectiveModelContext schemaContext)  throws IOException {
         final String moduleName = module.getName();
         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
@@ -231,110 +237,85 @@ public class DefinitionGenerator {
             stack.enterSchemaTree(childNode.getQName());
             // For every container and list in the module
             if (childNode instanceof ContainerSchemaNode || childNode instanceof ListSchemaNode) {
-                if (childNode.isConfiguration()) {
-                    processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
-                            true, stack);
-                }
                 processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
-                        false, stack);
-                processActionNodeContainer(childNode, moduleName, definitions, definitionNames, stack);
+                    stack, module, true);
+                processActionNodeContainer(childNode, moduleName, definitions, definitionNames, stack, module);
             }
             stack.exit();
         }
     }
 
-    private void processActionNodeContainer(final DataSchemaNode childNode, final String moduleName,
+    private static void processActionNodeContainer(final DataSchemaNode childNode, final String moduleName,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final SchemaInferenceStack stack) throws IOException {
+            final SchemaInferenceStack stack, final Module module) throws IOException {
         for (final ActionDefinition actionDef : ((ActionNodeContainer) childNode).getActions()) {
             stack.enterSchemaTree(actionDef.getQName());
-            processOperations(actionDef, moduleName, definitions, definitionNames, stack);
+            processOperations(actionDef, moduleName, definitions, definitionNames, stack, module);
             stack.exit();
         }
     }
 
-    private void processRPCs(final Module module, final Map<String, Schema> definitions,
+    private static void processRPCs(final Module module, final Map<String, Schema> definitions,
             final DefinitionNames definitionNames, final EffectiveModelContext schemaContext) throws IOException {
         final String moduleName = module.getName();
         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
         for (final RpcDefinition rpcDefinition : module.getRpcs()) {
             stack.enterSchemaTree(rpcDefinition.getQName());
-            processOperations(rpcDefinition, moduleName, definitions, definitionNames, stack);
+            processOperations(rpcDefinition, moduleName, definitions, definitionNames, stack, module);
             stack.exit();
         }
     }
 
-    private void processOperations(final OperationDefinition operationDef, final String parentName,
+    private static void processOperations(final OperationDefinition operationDef, final String parentName,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final SchemaInferenceStack stack) throws IOException {
+            final SchemaInferenceStack stack, final Module module) throws IOException {
         final String operationName = operationDef.getQName().getLocalName();
         processOperationInputOutput(operationDef.getInput(), operationName, parentName, true, definitions,
-                definitionNames, stack);
+                definitionNames, stack, module);
         processOperationInputOutput(operationDef.getOutput(), operationName, parentName, false, definitions,
-                definitionNames, stack);
+                definitionNames, stack, module);
     }
 
-    private void processOperationInputOutput(final ContainerLike container, final String operationName,
+    private static void processOperationInputOutput(final ContainerLike container, final String operationName,
             final String parentName, final boolean isInput, final Map<String, Schema> definitions,
-            final DefinitionNames definitionNames, final SchemaInferenceStack stack)
-            throws IOException {
+            final DefinitionNames definitionNames, final SchemaInferenceStack stack,
+            final Module module) throws IOException {
         stack.enterSchemaTree(container.getQName());
         if (!container.getChildNodes().isEmpty()) {
             final String filename = parentName + "_" + operationName + (isInput ? INPUT_SUFFIX : OUTPUT_SUFFIX);
-            final Schema childSchema = new Schema();
-            processChildren(childSchema, container.getChildNodes(), parentName, definitions, definitionNames,
-                    false, stack);
-
-            childSchema.setType(OBJECT_TYPE);
-            final ObjectNode xml = JsonNodeFactory.instance.objectNode();
-            xml.put(NAME_KEY, isInput ? INPUT : OUTPUT);
-            childSchema.setXml(xml);
-            childSchema.setTitle(filename);
+            final Schema.Builder childSchemaBuilder = new Schema.Builder()
+                .title(filename)
+                .type(OBJECT_TYPE)
+                .xml(new Xml(isInput ? INPUT : OUTPUT, null, null));
+            processChildren(childSchemaBuilder, container.getChildNodes(), parentName, definitions, definitionNames,
+                stack, module, false);
             final String discriminator =
-                    definitionNames.pickDiscriminator(container, List.of(filename, filename + TOP));
-            definitions.put(filename + discriminator, childSchema);
-
-            processTopData(filename, discriminator, definitions, container);
+                definitionNames.pickDiscriminator(container, List.of(filename));
+            definitions.put(filename + discriminator, childSchemaBuilder.build());
         }
         stack.exit();
     }
 
-    private static ObjectNode processTopData(final String filename, final String discriminator,
-            final Map<String, Schema> definitions, final SchemaNode schemaNode) {
-        final ObjectNode dataNodeProperties = JsonNodeFactory.instance.objectNode();
+    private static Property processRef(final String filename, final String discriminator,
+            final SchemaNode schemaNode) {
+        final Property.Builder dataNodeProperties = new Property.Builder();
         final String name = filename + discriminator;
         final String ref = COMPONENTS_PREFIX + name;
-        final String topName = filename + TOP;
 
         if (schemaNode instanceof ListSchemaNode) {
-            dataNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
-            final ObjectNode items = JsonNodeFactory.instance.objectNode();
-            items.put(REF_KEY, ref);
-            dataNodeProperties.set(ITEMS_KEY, items);
-            dataNodeProperties.put(DESCRIPTION_KEY, schemaNode.getDescription().orElse(""));
+            dataNodeProperties.type(ARRAY_TYPE);
+            final Property items = new Property.Builder().ref(ref).build();
+            dataNodeProperties.items(items);
+            dataNodeProperties.description(schemaNode.getDescription().orElse(""));
         } else {
              /*
                 Description can't be added, because nothing allowed alongside $ref.
                 allOf is not an option, because ServiceNow can't parse it.
               */
-            dataNodeProperties.put(REF_KEY, ref);
+            dataNodeProperties.ref(ref);
         }
 
-        final ObjectNode properties = JsonNodeFactory.instance.objectNode();
-        /*
-            Add module name prefix to property name, when needed, when ServiceNow can process colons,
-            use RestDocGenUtil#resolveNodesName for creating property name
-         */
-        properties.set(schemaNode.getQName().getLocalName(), dataNodeProperties);
-        final Schema finalChildSchema = new Schema();
-        finalChildSchema.setType(OBJECT_TYPE);
-        finalChildSchema.setProperties(properties);
-        finalChildSchema.setTitle(topName);
-
-
-        definitions.put(topName + discriminator, finalChildSchema);
-
-        return dataNodeProperties;
+        return dataNodeProperties.build();
     }
 
     /**
@@ -359,174 +340,192 @@ public class DefinitionGenerator {
     }
 
     private static void populateEnumWithDerived(final Collection<? extends IdentitySchemaNode> derivedIds,
-            final ArrayNode enumPayload, final EffectiveModelContext context) {
+            final List<String> enumPayload, final EffectiveModelContext context) {
         for (final IdentitySchemaNode derivedId : derivedIds) {
             enumPayload.add(derivedId.getQName().getLocalName());
             populateEnumWithDerived(context.getDerivedIdentities(derivedId), enumPayload, context);
         }
     }
 
-    private ObjectNode processDataNodeContainer(final DataNodeContainer dataNode, final String parentName,
-            final Map<String, Schema> definitions, final DefinitionNames definitionNames, final boolean isConfig,
-            final SchemaInferenceStack stack) throws IOException {
-        if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
-            final Collection<? extends DataSchemaNode> containerChildren = dataNode.getChildNodes();
-            final SchemaNode schemaNode = (SchemaNode) dataNode;
-            final String localName = schemaNode.getQName().getLocalName();
-            final Schema childSchema = new Schema();
-            final String nameAsParent = parentName + "_" + localName;
-            final ObjectNode properties =
-                    processChildren(childSchema, containerChildren, parentName + "_" + localName, definitions,
-                            definitionNames, isConfig, stack);
-
-            final String nodeName = parentName + (isConfig ? CONFIG : "") + "_" + localName;
-            final String parentNameConfigLocalName = parentName + CONFIG + "_" + localName;
-
-            final String description = schemaNode.getDescription().orElse("");
-            final String discriminator;
-
-            if (!definitionNames.isListedNode(schemaNode)) {
-                final List<String> names = List.of(parentNameConfigLocalName,
-                        parentNameConfigLocalName + TOP,
-                        nameAsParent,
-                        nameAsParent + TOP);
-                discriminator = definitionNames.pickDiscriminator(schemaNode, names);
-            } else {
-                discriminator = definitionNames.getDiscriminator(schemaNode);
-            }
-
-            childSchema.setType(OBJECT_TYPE);
-            childSchema.setProperties(properties);
-            childSchema.setTitle(nodeName);
-            childSchema.setDescription(description);
+    private static Property processDataNodeContainer(final DataNodeContainer dataNode, final String parentName,
+            final Map<String, Schema> definitions, final DefinitionNames definitionNames,
+            final SchemaInferenceStack stack, final Module module, final boolean isParentConfig) throws IOException {
+        final Collection<? extends DataSchemaNode> containerChildren = dataNode.getChildNodes();
+        final SchemaNode schemaNode = (SchemaNode) dataNode;
+        final String localName = schemaNode.getQName().getLocalName();
+        final String nodeName = parentName + "_" + localName;
+        final Schema.Builder childSchemaBuilder = new Schema.Builder()
+            .type(OBJECT_TYPE)
+            .title(nodeName)
+            .description(schemaNode.getDescription().orElse(""));
+        final boolean isConfig = ((DataSchemaNode) dataNode).isConfiguration() && isParentConfig;
+        childSchemaBuilder.properties(processChildren(childSchemaBuilder, containerChildren,
+            parentName + "_" + localName, definitions, definitionNames, stack, module, isConfig));
+
+        final String discriminator;
+        if (!definitionNames.isListedNode(schemaNode)) {
+            final String parentNameConfigLocalName = parentName + "_" + localName;
+            final List<String> names = List.of(parentNameConfigLocalName);
+            discriminator = definitionNames.pickDiscriminator(schemaNode, names);
+        } else {
+            discriminator = definitionNames.getDiscriminator(schemaNode);
+        }
 
-            final String defName = nodeName + discriminator;
-            childSchema.setXml(buildXmlParameter(schemaNode));
-            definitions.put(defName, childSchema);
+        final String defName = nodeName + discriminator;
+        childSchemaBuilder.xml(buildXmlParameter(schemaNode));
+        definitions.put(defName, childSchemaBuilder.build());
 
-            return processTopData(nodeName, discriminator, definitions, schemaNode);
-        }
-        return null;
+        return processRef(nodeName, discriminator, schemaNode);
     }
 
     /**
      * Processes the nodes.
      */
-    private ObjectNode processChildren(final Schema parentNode, final Collection<? extends DataSchemaNode> nodes,
-            final String parentName, final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final boolean isConfig, final SchemaInferenceStack stack) throws IOException {
-        final ObjectNode properties = JsonNodeFactory.instance.objectNode();
-        final ArrayNode required = JsonNodeFactory.instance.arrayNode();
+    private static Map<String, Property> processChildren(final Schema.Builder parentNodeBuilder,
+            final Collection<? extends DataSchemaNode> nodes, final String parentName,
+            final Map<String, Schema> definitions, final DefinitionNames definitionNames,
+            final SchemaInferenceStack stack, final Module module, final boolean isParentConfig) throws IOException {
+        final Map<String, Property> properties = new HashMap<>();
+        final List<String> required = new ArrayList<>();
         for (final DataSchemaNode node : nodes) {
-            if (!isConfig || node.isConfiguration()) {
-                processChildNode(node, parentName, definitions, definitionNames, isConfig, stack, properties);
+            if (node instanceof ChoiceSchemaNode choice) {
+                stack.enterSchemaTree(node.getQName());
+                final boolean isConfig = isParentConfig && node.isConfiguration();
+                final Map<String, Property> choiceProperties = processChoiceNodeRecursively(parentName,
+                    definitions, definitionNames, isConfig, stack, required, choice, module);
+                properties.putAll(choiceProperties);
+                stack.exit();
+            } else {
+                final Property property = processChildNode(node, parentName, definitions, definitionNames,
+                    stack, required, module, isParentConfig);
+                if (property != null) {
+                    properties.put(node.getQName().getLocalName(), property);
+                }
             }
         }
-        parentNode.setProperties(properties);
-        setRequiredIfNotEmpty(parentNode, required);
+        parentNodeBuilder.properties(properties).required(required.size() > 0 ? required : null);
         return properties;
     }
 
-    private void processChildNode(final DataSchemaNode node, final String parentName,
+    private static Map<String, Property> processChoiceNodeRecursively(final String parentName,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames, final boolean isConfig,
-            final SchemaInferenceStack stack, final ObjectNode properties) throws IOException {
+            final SchemaInferenceStack stack, final List<String> required, final ChoiceSchemaNode choice,
+            final Module module) throws IOException {
+        if (!choice.getCases().isEmpty()) {
+            final var properties = new HashMap<String, Property>();
+            final var caseSchemaNode = choice.getDefaultCase().orElse(choice.getCases().stream()
+                .findFirst().orElseThrow());
+            stack.enterSchemaTree(caseSchemaNode.getQName());
+            for (final var childNode : caseSchemaNode.getChildNodes()) {
+                if (childNode instanceof ChoiceSchemaNode childChoice) {
+                    final var isChildConfig = isConfig && childNode.isConfiguration();
+                    stack.enterSchemaTree(childNode.getQName());
+                    final var childProperties = processChoiceNodeRecursively(parentName, definitions, definitionNames,
+                        isChildConfig, stack, required, childChoice, module);
+                    properties.putAll(childProperties);
+                    stack.exit();
+                } else {
+                    final var property = processChildNode(childNode, parentName, definitions, definitionNames, stack,
+                        required, module, isConfig);
+                    if (property != null) {
+                        properties.put(childNode.getQName().getLocalName(), property);
+                    }
+                }
+            }
+            stack.exit();
+            return properties;
+        }
+        return Map.of();
+    }
 
+    private static Property processChildNode(final DataSchemaNode node, final String parentName,
+            final Map<String, Schema> definitions, final DefinitionNames definitionNames,
+            final SchemaInferenceStack stack, final List<String> required, final Module module,
+            final boolean isParentConfig) throws IOException {
+        final XMLNamespace parentNamespace = stack.toSchemaNodeIdentifier().lastNodeIdentifier().getNamespace();
         stack.enterSchemaTree(node.getQName());
-
         /*
             Add module name prefix to property name, when needed, when ServiceNow can process colons,
             use RestDocGenUtil#resolveNodesName for creating property name
          */
         final String name = node.getQName().getLocalName();
-
-        if (node instanceof LeafSchemaNode leaf) {
-            processLeafNode(leaf, name, properties, JsonNodeFactory.instance.arrayNode(), stack, definitions,
-                    definitionNames);
-
-        } else if (node instanceof AnyxmlSchemaNode anyxml) {
-            processAnyXMLNode(anyxml, name, properties, JsonNodeFactory.instance.arrayNode());
-
-        } else if (node instanceof AnydataSchemaNode anydata) {
-            processAnydataNode(anydata, name, properties, JsonNodeFactory.instance.arrayNode());
-
-        } else {
-
-            final ObjectNode property;
-            if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
-                property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions,
-                        definitionNames, isConfig, stack);
-                if (!isConfig) {
-                    processActionNodeContainer(node, parentName, definitions, definitionNames, stack);
+        /*
+            If the parent is operational, then current node is also operational and should be added as a child
+            even if node.isConfiguration()==true.
+            If the parent is configuration, then current node should be added as a child only if
+            node.isConfiguration()==true.
+        */
+        final boolean shouldBeAddedAsChild = !isParentConfig || node.isConfiguration();
+        Property property = null;
+        if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
+            final Property dataNodeContainer = processDataNodeContainer((DataNodeContainer) node, parentName,
+                definitions, definitionNames, stack, module, isParentConfig);
+            if (shouldBeAddedAsChild) {
+                if (isSchemaNodeMandatory(node)) {
+                    required.add(name);
                 }
+                property = dataNodeContainer;
+            }
+            processActionNodeContainer(node, parentName, definitions, definitionNames, stack, module);
+        } else if (shouldBeAddedAsChild) {
+            if (node instanceof LeafSchemaNode leaf) {
+                property = processLeafNode(leaf, name, required, stack, definitions, definitionNames, parentNamespace,
+                    module);
+            } else if (node instanceof AnyxmlSchemaNode || node instanceof AnydataSchemaNode) {
+                property = processUnknownDataSchemaNode(node, name, required, parentNamespace);
             } else if (node instanceof LeafListSchemaNode leafList) {
-                property = processLeafListNode(leafList, stack, definitions, definitionNames);
-
-            } else if (node instanceof ChoiceSchemaNode choice) {
-                if (!choice.getCases().isEmpty()) {
-                    CaseSchemaNode caseSchemaNode = choice.getDefaultCase()
-                            .orElse(choice.getCases().stream()
-                                    .findFirst().orElseThrow());
-                    stack.enterSchemaTree(caseSchemaNode.getQName());
-                    for (final DataSchemaNode childNode : caseSchemaNode.getChildNodes()) {
-                        processChildNode(childNode, parentName, definitions, definitionNames, isConfig, stack,
-                                properties);
-                    }
-                    stack.exit();
+                if (isSchemaNodeMandatory(node)) {
+                    required.add(name);
                 }
-                property = null;
-
+                property = processLeafListNode(leafList, stack, definitions, definitionNames, module);
             } else {
                 throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
             }
-            if (property != null) {
-                properties.set(name, property);
-            }
         }
-
         stack.exit();
+        return property;
     }
 
-    private ObjectNode processLeafListNode(final LeafListSchemaNode listNode, final SchemaInferenceStack stack,
-            final Map<String, Schema> definitions, final DefinitionNames definitionNames) {
-        final ObjectNode props = JsonNodeFactory.instance.objectNode();
-        props.put(TYPE_KEY, ARRAY_TYPE);
+    private static Property processLeafListNode(final LeafListSchemaNode listNode,
+            final SchemaInferenceStack stack, final Map<String, Schema> definitions,
+            final DefinitionNames definitionNames, final Module module) {
+        final Property.Builder props = new Property.Builder();
+        props.type(ARRAY_TYPE);
 
-        final ObjectNode itemsVal = JsonNodeFactory.instance.objectNode();
+        final Property.Builder itemsVal = new Property.Builder();
         final Optional<ElementCountConstraint> optConstraint = listNode.getElementCountConstraint();
-        processElementCount(optConstraint, props);
+        optConstraint.ifPresent(elementCountConstraint -> processElementCount(elementCountConstraint, props));
 
-        processTypeDef(listNode.getType(), listNode, itemsVal, stack, definitions, definitionNames);
-        props.set(ITEMS_KEY, itemsVal);
+        processTypeDef(listNode.getType(), listNode, itemsVal, stack, definitions, definitionNames, module);
 
-        props.put(DESCRIPTION_KEY, listNode.getDescription().orElse(""));
+        props.items(itemsVal.build());
+        props.description(listNode.getDescription().orElse(""));
 
-        return props;
+        return props.build();
     }
 
-    private static void processElementCount(final Optional<ElementCountConstraint> constraint, final ObjectNode props) {
-        if (constraint.isPresent()) {
-            final ElementCountConstraint constr = constraint.orElseThrow();
-            final Integer minElements = constr.getMinElements();
-            if (minElements != null) {
-                props.put(MIN_ITEMS, minElements);
-            }
-            final Integer maxElements = constr.getMaxElements();
-            if (maxElements != null) {
-                props.put(MAX_ITEMS, maxElements);
-            }
+    private static void processElementCount(final ElementCountConstraint constraint, final Property.Builder props) {
+        final Integer minElements = constraint.getMinElements();
+        if (minElements != null) {
+            props.minItems(minElements);
+        }
+        final Integer maxElements = constraint.getMaxElements();
+        if (maxElements != null) {
+            props.maxItems(maxElements);
         }
     }
 
-    private static void processMandatory(final MandatoryAware node, final String nodeName, final ArrayNode required) {
+    private static void processMandatory(final MandatoryAware node, final String nodeName,
+            final List<String> required) {
         if (node.isMandatory()) {
             required.add(nodeName);
         }
     }
 
-    private ObjectNode processLeafNode(final LeafSchemaNode leafNode, final String jsonLeafName,
-            final ObjectNode properties, final ArrayNode required, final SchemaInferenceStack stack,
-            final Map<String, Schema> definitions, final DefinitionNames definitionNames) {
-        final ObjectNode property = JsonNodeFactory.instance.objectNode();
+    private static Property processLeafNode(final LeafSchemaNode leafNode, final String jsonLeafName,
+            final List<String> required, final SchemaInferenceStack stack, final Map<String, Schema> definitions,
+            final DefinitionNames definitionNames, final XMLNamespace parentNamespace, final Module module) {
+        final Property.Builder property = new Property.Builder();
 
         final String leafDescription = leafNode.getDescription().orElse("");
         /*
@@ -534,146 +533,133 @@ public class DefinitionGenerator {
             allOf is not an option, because ServiceNow can't parse it.
         */
         if (!(leafNode.getType() instanceof IdentityrefTypeDefinition)) {
-            property.put(DESCRIPTION_KEY, leafDescription);
+            property.description(leafDescription);
         }
 
-        processTypeDef(leafNode.getType(), leafNode, property, stack, definitions, definitionNames);
-        properties.set(jsonLeafName, property);
-        property.set(XML_KEY, buildXmlParameter(leafNode));
+        processTypeDef(leafNode.getType(), leafNode, property, stack, definitions, definitionNames, module);
+        if (!leafNode.getQName().getNamespace().equals(parentNamespace)) {
+            // If the parent is not from the same model, define the child XML namespace.
+            property.xml(buildXmlParameter(leafNode));
+        }
         processMandatory(leafNode, jsonLeafName, required);
-
-        return property;
+        return property.build();
     }
 
-    private static ObjectNode processAnydataNode(final AnydataSchemaNode leafNode, final String name,
-            final ObjectNode properties, final ArrayNode required) {
-        final ObjectNode property = JsonNodeFactory.instance.objectNode();
-
-        final String leafDescription = leafNode.getDescription().orElse("");
-        property.put(DESCRIPTION_KEY, leafDescription);
+    private static Property processUnknownDataSchemaNode(final DataSchemaNode leafNode, final String name,
+            final List<String> required, final XMLNamespace parentNamespace) {
+        assert (leafNode instanceof AnydataSchemaNode || leafNode instanceof AnyxmlSchemaNode);
 
-        final String localName = leafNode.getQName().getLocalName();
-        setDefaultValue(property, String.format("<%s> ... </%s>", localName, localName));
-        property.put(TYPE_KEY, STRING_TYPE);
-        property.set(XML_KEY, buildXmlParameter(leafNode));
-        processMandatory(leafNode, name, required);
-        properties.set(name, property);
-
-        return property;
-    }
-
-    private static ObjectNode processAnyXMLNode(final AnyxmlSchemaNode leafNode, final String name,
-            final ObjectNode properties, final ArrayNode required) {
-        final ObjectNode property = JsonNodeFactory.instance.objectNode();
+        final Property.Builder property = new Property.Builder();
 
         final String leafDescription = leafNode.getDescription().orElse("");
-        property.put(DESCRIPTION_KEY, leafDescription);
+        property.description(leafDescription);
 
         final String localName = leafNode.getQName().getLocalName();
-        setDefaultValue(property, String.format("<%s> ... </%s>", localName, localName));
-        property.put(TYPE_KEY, STRING_TYPE);
-        property.set(XML_KEY, buildXmlParameter(leafNode));
-        processMandatory(leafNode, name, required);
-        properties.set(name, property);
-
-        return property;
+        property.example(String.format("<%s> ... </%s>", localName, localName));
+        property.type(STRING_TYPE);
+        if (!leafNode.getQName().getNamespace().equals(parentNamespace)) {
+            // If the parent is not from the same model, define the child XML namespace.
+            property.xml(buildXmlParameter(leafNode));
+        }
+        processMandatory((MandatoryAware) leafNode, name, required);
+        return property.build();
     }
 
-    private String processTypeDef(final TypeDefinition<?> leafTypeDef, final DataSchemaNode node,
-            final ObjectNode property, final SchemaInferenceStack stack, final Map<String, Schema> definitions,
-            final DefinitionNames definitionNames) {
+    private static String processTypeDef(final TypeDefinition<?> leafTypeDef, final DataSchemaNode node,
+            final Property.Builder property, final SchemaInferenceStack stack,final Map<String, Schema> definitions,
+            final DefinitionNames definitionNames, final Module module) {
         final String jsonType;
-        if (leafTypeDef instanceof BinaryTypeDefinition) {
-            jsonType = processBinaryType(property);
-
-        } else if (leafTypeDef instanceof BitsTypeDefinition) {
-            jsonType = processBitsType((BitsTypeDefinition) leafTypeDef, property);
-
-        } else if (leafTypeDef instanceof EnumTypeDefinition) {
-            jsonType = processEnumType((EnumTypeDefinition) leafTypeDef, property);
-
-        } else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
-            jsonType = processIdentityRefType((IdentityrefTypeDefinition) leafTypeDef, property, definitions,
-                    definitionNames, stack.getEffectiveModelContext());
-
-        } else if (leafTypeDef instanceof StringTypeDefinition) {
-            jsonType = processStringType(leafTypeDef, property, node.getQName().getLocalName());
-
-        } else if (leafTypeDef instanceof UnionTypeDefinition) {
-            jsonType = processUnionType((UnionTypeDefinition) leafTypeDef);
-
+        if (leafTypeDef instanceof BinaryTypeDefinition binaryType) {
+            jsonType = processBinaryType(binaryType, property);
+        } else if (leafTypeDef instanceof BitsTypeDefinition bitsType) {
+            jsonType = processBitsType(bitsType, property);
+        } else if (leafTypeDef instanceof EnumTypeDefinition enumType) {
+            jsonType = processEnumType(enumType, property);
+        } else if (leafTypeDef instanceof IdentityrefTypeDefinition identityrefType) {
+            jsonType = processIdentityRefType(identityrefType, property, definitions,
+                    definitionNames, stack.getEffectiveModelContext(), module);
+        } else if (leafTypeDef instanceof StringTypeDefinition stringType) {
+            jsonType = processStringType(stringType, property, node.getQName().getLocalName());
+        } else if (leafTypeDef instanceof UnionTypeDefinition unionType) {
+            jsonType = processTypeDef(unionType.getTypes().iterator().next(), node, property, stack, definitions,
+                definitionNames, module);
         } else if (leafTypeDef instanceof EmptyTypeDefinition) {
             jsonType = OBJECT_TYPE;
-        } else if (leafTypeDef instanceof LeafrefTypeDefinition) {
-            return processTypeDef(stack.resolveLeafref((LeafrefTypeDefinition) leafTypeDef), node, property,
-                stack, definitions, definitionNames);
+        } else if (leafTypeDef instanceof LeafrefTypeDefinition leafrefType) {
+            return processTypeDef(stack.resolveLeafref(leafrefType), node, property,
+                stack, definitions, definitionNames, module);
         } else if (leafTypeDef instanceof BooleanTypeDefinition) {
             jsonType = BOOLEAN_TYPE;
-            setDefaultValue(property, true);
-        } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
-            jsonType = processNumberType((RangeRestrictedTypeDefinition<?, ?>) leafTypeDef, property);
-        } else if (leafTypeDef instanceof InstanceIdentifierTypeDefinition) {
-            jsonType = processInstanceIdentifierType(node, property, stack.getEffectiveModelContext());
+            leafTypeDef.getDefaultValue().ifPresent(v -> property.defaultValue(Boolean.valueOf((String) v)));
+            property.example(true);
+        } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition<?, ?> rangeRestrictedType) {
+            jsonType = processNumberType(rangeRestrictedType, property);
+        } else if (leafTypeDef instanceof InstanceIdentifierTypeDefinition instanceIdentifierType) {
+            jsonType = processInstanceIdentifierType(instanceIdentifierType, node, property,
+                stack.getEffectiveModelContext());
         } else {
             jsonType = STRING_TYPE;
         }
         if (!(leafTypeDef instanceof IdentityrefTypeDefinition)) {
-            putIfNonNull(property, TYPE_KEY, jsonType);
+            if (TYPE_KEY != null && jsonType != null) {
+                property.type(jsonType);
+            }
             if (leafTypeDef.getDefaultValue().isPresent()) {
                 final Object defaultValue = leafTypeDef.getDefaultValue().orElseThrow();
                 if (defaultValue instanceof String stringDefaultValue) {
                     if (leafTypeDef instanceof BooleanTypeDefinition) {
-                        setDefaultValue(property, Boolean.valueOf(stringDefaultValue));
+                        property.defaultValue(Boolean.valueOf(stringDefaultValue));
                     } else if (leafTypeDef instanceof DecimalTypeDefinition
                             || leafTypeDef instanceof Uint64TypeDefinition) {
-                        setDefaultValue(property, new BigDecimal(stringDefaultValue));
-                    } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
+                        property.defaultValue(new BigDecimal(stringDefaultValue));
+                    } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition<?, ?> rangeRestrictedType) {
                         //uint8,16,32 int8,16,32,64
-                        if (isHexadecimalOrOctal((RangeRestrictedTypeDefinition<?, ?>)leafTypeDef)) {
-                            setDefaultValue(property, stringDefaultValue);
+                        if (isHexadecimalOrOctal(rangeRestrictedType)) {
+                            property.defaultValue(stringDefaultValue);
                         } else {
-                            setDefaultValue(property, Long.valueOf(stringDefaultValue));
+                            property.defaultValue(Long.valueOf(stringDefaultValue));
                         }
                     } else {
-                        setDefaultValue(property, stringDefaultValue);
+                        property.defaultValue(stringDefaultValue);
                     }
                 } else {
                     //we should never get here. getDefaultValue always gives us string
-                    setDefaultValue(property, defaultValue.toString());
+                    property.defaultValue(defaultValue.toString());
                 }
             }
         }
         return jsonType;
     }
 
-    private static String processBinaryType(final ObjectNode property) {
-        property.put(FORMAT_KEY, "byte");
+    private static String processBinaryType(final BinaryTypeDefinition definition, final Property.Builder property) {
+        definition.getDefaultValue().ifPresent(property::defaultValue);
+        property.format("byte");
         return STRING_TYPE;
     }
 
-    private static String processEnumType(final EnumTypeDefinition enumLeafType, final ObjectNode property) {
+    private static String processEnumType(final EnumTypeDefinition enumLeafType, final Property.Builder property) {
         final List<EnumPair> enumPairs = enumLeafType.getValues();
-        final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
-        for (final EnumPair enumPair : enumPairs) {
-            enumNames.add(new TextNode(enumPair.getName()));
-        }
+        final List<String> enumNames = enumPairs.stream()
+            .map(EnumPair::getName)
+            .toList();
 
-        property.set(ENUM_KEY, enumNames);
-        setDefaultValue(property, enumLeafType.getValues().iterator().next().getName());
+        property.enums(enumNames);
+        enumLeafType.getDefaultValue().ifPresent(property::defaultValue);
+        property.example(enumLeafType.getValues().iterator().next().getName());
         return STRING_TYPE;
     }
 
-    private String processIdentityRefType(final IdentityrefTypeDefinition leafTypeDef, final ObjectNode property,
-            final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final EffectiveModelContext schemaContext) {
+    private static String processIdentityRefType(final IdentityrefTypeDefinition leafTypeDef,
+            final Property.Builder property, final Map<String, Schema> definitions,
+            final DefinitionNames definitionNames, final EffectiveModelContext schemaContext, final Module module) {
         final String definitionName;
-        if (isImported(leafTypeDef)) {
+        if (isImported(leafTypeDef, module)) {
             definitionName = addImportedIdentity(leafTypeDef, definitions, definitionNames, schemaContext);
         } else {
             final SchemaNode node = leafTypeDef.getIdentities().iterator().next();
             definitionName = node.getQName().getLocalName() + definitionNames.getDiscriminator(node);
         }
-        property.put(REF_KEY, COMPONENTS_PREFIX + definitionName);
+        property.ref(COMPONENTS_PREFIX + definitionName);
         return STRING_TYPE;
     }
 
@@ -693,56 +679,53 @@ public class DefinitionGenerator {
         }
     }
 
-    private static Schema buildIdentityObject(final IdentitySchemaNode idNode,
-            final EffectiveModelContext context) {
-        final Schema identityObj = new Schema();
+    private static Schema buildIdentityObject(final IdentitySchemaNode idNode, final EffectiveModelContext context) {
         final String identityName = idNode.getQName().getLocalName();
         LOG.debug("Processing Identity: {}", identityName);
 
-        identityObj.setTitle(identityName);
-        identityObj.setDescription(idNode.getDescription().orElse(""));
-
         final Collection<? extends IdentitySchemaNode> derivedIds = context.getDerivedIdentities(idNode);
-
-        final ArrayNode enumPayload = JsonNodeFactory.instance.arrayNode();
+        final List<String> enumPayload = new ArrayList<>();
         enumPayload.add(identityName);
         populateEnumWithDerived(derivedIds, enumPayload, context);
-        identityObj.setEnum(enumPayload);
-        identityObj.setType(STRING_TYPE);
-        return identityObj;
+        final List<String> schemaEnum = new ArrayList<>(enumPayload);
+
+        return new Schema.Builder()
+            .title(identityName)
+            .description(idNode.getDescription().orElse(""))
+            .schemaEnum(schemaEnum)
+            .type(STRING_TYPE)
+            .build();
     }
 
-    private boolean isImported(final IdentityrefTypeDefinition leafTypeDef) {
-        return !leafTypeDef.getQName().getModule().equals(topLevelModule.getQNameModule());
+    private static boolean isImported(final IdentityrefTypeDefinition leafTypeDef, final Module module) {
+        return !leafTypeDef.getQName().getModule().equals(module.getQNameModule());
     }
 
-    private static String processBitsType(final BitsTypeDefinition bitsType, final ObjectNode property) {
-        property.put(MIN_ITEMS, 0);
-        property.put(UNIQUE_ITEMS_KEY, true);
-        final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
+    private static String processBitsType(final BitsTypeDefinition bitsType, final Property.Builder property) {
+        property.minItems(0);
+        property.uniqueItems(true);
         final Collection<? extends Bit> bits = bitsType.getBits();
-        for (final Bit bit : bits) {
-            enumNames.add(new TextNode(bit.getName()));
-        }
-        property.set(ENUM_KEY, enumNames);
-        property.put(DEFAULT_KEY, enumNames.iterator().next() + " " + enumNames.get(enumNames.size() - 1));
+        final List<String> enumNames = bits.stream()
+            .map(Bit::getName)
+            .toList();
+        property.enums(enumNames);
+        property.defaultValue(enumNames.iterator().next() + " " + enumNames.get(enumNames.size() - 1));
+        bitsType.getDefaultValue().ifPresent(property::defaultValue);
         return STRING_TYPE;
     }
 
-    private static String processStringType(final TypeDefinition<?> stringType, final ObjectNode property,
+    private static String processStringType(final StringTypeDefinition stringType, final Property.Builder property,
             final String nodeName) {
-        StringTypeDefinition type = (StringTypeDefinition) stringType;
-        Optional<LengthConstraint> lengthConstraints = ((StringTypeDefinition) stringType).getLengthConstraint();
-        while (lengthConstraints.isEmpty() && type.getBaseType() != null) {
+        var type = stringType;
+        while (type.getLengthConstraint().isEmpty() && type.getBaseType() != null) {
             type = type.getBaseType();
-            lengthConstraints = type.getLengthConstraint();
         }
 
-        if (lengthConstraints.isPresent()) {
-            final Range<Integer> range = lengthConstraints.orElseThrow().getAllowedRanges().span();
-            putIfNonNull(property, MIN_LENGTH_KEY, range.lowerEndpoint());
-            putIfNonNull(property, MAX_LENGTH_KEY, range.upperEndpoint());
-        }
+        type.getLengthConstraint().ifPresent(constraint -> {
+            final Range<Integer> range = constraint.getAllowedRanges().span();
+            property.minLength(range.lowerEndpoint());
+            property.maxLength(range.upperEndpoint());
+        });
 
         if (type.getPatternConstraints().iterator().hasNext()) {
             final PatternConstraint pattern = type.getPatternConstraints().iterator().next();
@@ -759,15 +742,17 @@ public class DefinitionGenerator {
             } catch (IllegalArgumentException ex) {
                 LOG.warn("Cannot create example string for type: {} with regex: {}.", stringType.getQName(), regex);
             }
-            setDefaultValue(property, defaultValue);
+            property.example(defaultValue);
         } else {
-            setDefaultValue(property, "Some " + nodeName);
+            property.example("Some " + nodeName);
         }
+
+        stringType.getDefaultValue().ifPresent(property::defaultValue);
         return STRING_TYPE;
     }
 
     private static String processNumberType(final RangeRestrictedTypeDefinition<?, ?> leafTypeDef,
-            final ObjectNode property) {
+            final Property.Builder property) {
         final Optional<Number> maybeLower = leafTypeDef.getRangeConstraint()
                 .map(RangeConstraint::getAllowedRanges).map(RangeSet::span).map(Range::lowerEndpoint);
 
@@ -776,7 +761,9 @@ public class DefinitionGenerator {
         }
 
         if (leafTypeDef instanceof DecimalTypeDefinition) {
-            maybeLower.ifPresent(number -> setDefaultValue(property, ((Decimal64) number).decimalValue()));
+            leafTypeDef.getDefaultValue().ifPresent(number -> property.defaultValue(Decimal64.valueOf((String) number)
+                .decimalValue()));
+            maybeLower.ifPresent(number -> property.example(((Decimal64) number).decimalValue()));
             return NUMBER_TYPE;
         }
         if (leafTypeDef instanceof Uint8TypeDefinition
@@ -785,16 +772,19 @@ public class DefinitionGenerator {
                 || leafTypeDef instanceof Int16TypeDefinition
                 || leafTypeDef instanceof Int32TypeDefinition) {
 
-            property.put(FORMAT_KEY, INT32_FORMAT);
-            maybeLower.ifPresent(number -> setDefaultValue(property, Integer.valueOf(number.toString())));
+            property.format(INT32_FORMAT);
+            leafTypeDef.getDefaultValue().ifPresent(number -> property.defaultValue(Integer.valueOf((String) number)));
+            maybeLower.ifPresent(number -> property.example(Integer.valueOf(number.toString())));
         } else if (leafTypeDef instanceof Uint32TypeDefinition
                 || leafTypeDef instanceof Int64TypeDefinition) {
 
-            property.put(FORMAT_KEY, INT64_FORMAT);
-            maybeLower.ifPresent(number -> setDefaultValue(property, Long.valueOf(number.toString())));
+            property.format(INT64_FORMAT);
+            leafTypeDef.getDefaultValue().ifPresent(number -> property.defaultValue(Long.valueOf((String) number)));
+            maybeLower.ifPresent(number -> property.example(Long.valueOf(number.toString())));
         } else {
             //uint64
-            setDefaultValue(property, 0);
+            leafTypeDef.getDefaultValue().ifPresent(number -> property.defaultValue(new BigInteger((String) number)));
+            property.example(0);
         }
         return INTEGER_TYPE;
     }
@@ -808,108 +798,24 @@ public class DefinitionGenerator {
         return false;
     }
 
-    private static String processInstanceIdentifierType(final DataSchemaNode node, final ObjectNode property,
-            final EffectiveModelContext schemaContext) {
+    private static String processInstanceIdentifierType(final InstanceIdentifierTypeDefinition iidType,
+            final DataSchemaNode node, final Property.Builder property, final EffectiveModelContext schemaContext) {
         // create example instance-identifier to the first container of node's module if exists or leave it empty
         final var module = schemaContext.findModule(node.getQName().getModule());
         if (module.isPresent()) {
             final var container = module.orElseThrow().getChildNodes().stream()
                     .filter(n -> n instanceof ContainerSchemaNode)
                     .findFirst();
-            container.ifPresent(c -> setDefaultValue(property, String.format("/%s:%s", module.orElseThrow().getPrefix(),
-                    c.getQName().getLocalName())));
+            container.ifPresent(c -> property.example(String.format("/%s:%s", module.orElseThrow().getPrefix(),
+                c.getQName().getLocalName())));
         }
-
+        // set default value
+        iidType.getDefaultValue().ifPresent(property::defaultValue);
         return STRING_TYPE;
     }
 
-    private static String processUnionType(final UnionTypeDefinition unionType) {
-        boolean isStringTakePlace = false;
-        boolean isNumberTakePlace = false;
-        boolean isBooleanTakePlace = false;
-        for (final TypeDefinition<?> typeDef : unionType.getTypes()) {
-            if (!isStringTakePlace) {
-                if (typeDef instanceof StringTypeDefinition
-                        || typeDef instanceof BitsTypeDefinition
-                        || typeDef instanceof BinaryTypeDefinition
-                        || typeDef instanceof IdentityrefTypeDefinition
-                        || typeDef instanceof EnumTypeDefinition
-                        || typeDef instanceof LeafrefTypeDefinition
-                        || typeDef instanceof UnionTypeDefinition) {
-                    isStringTakePlace = true;
-                } else if (!isNumberTakePlace && typeDef instanceof RangeRestrictedTypeDefinition) {
-                    isNumberTakePlace = true;
-                } else if (!isBooleanTakePlace && typeDef instanceof BooleanTypeDefinition) {
-                    isBooleanTakePlace = true;
-                }
-            }
-        }
-        if (isStringTakePlace) {
-            return STRING_TYPE;
-        }
-        if (isBooleanTakePlace) {
-            if (isNumberTakePlace) {
-                return STRING_TYPE;
-            }
-            return BOOLEAN_TYPE;
-        }
-        return NUMBER_TYPE;
-    }
-
-    private static ObjectNode buildXmlParameter(final SchemaNode node) {
-        final ObjectNode xml = JsonNodeFactory.instance.objectNode();
+    private static Xml buildXmlParameter(final SchemaNode node) {
         final QName qName = node.getQName();
-        xml.put(NAME_KEY, qName.getLocalName());
-        xml.put(NAMESPACE_KEY, qName.getNamespace().toString());
-        return xml;
-    }
-
-    private static void putIfNonNull(final ObjectNode property, final String key, final Number number) {
-        if (key != null && number != null) {
-            if (number instanceof Double) {
-                property.put(key, (Double) number);
-            } else if (number instanceof Float) {
-                property.put(key, (Float) number);
-            } else if (number instanceof Integer) {
-                property.put(key, (Integer) number);
-            } else if (number instanceof Short) {
-                property.put(key, (Short) number);
-            } else if (number instanceof Long) {
-                property.put(key, (Long) number);
-            }
-        }
-    }
-
-    private static void putIfNonNull(final ObjectNode property, final String key, final String value) {
-        if (key != null && value != null) {
-            property.put(key, value);
-        }
+        return new Xml(qName.getLocalName(), qName.getNamespace().toString(), null);
     }
-
-    private static void setRequiredIfNotEmpty(final Schema node, final ArrayNode required) {
-        if (required.size() > 0) {
-            node.setRequired(required);
-        }
-    }
-
-    private static void setDefaultValue(final ObjectNode property, final String value) {
-        property.put(DEFAULT_KEY, value);
-    }
-
-    private static void setDefaultValue(final ObjectNode property, final Integer value) {
-        property.put(DEFAULT_KEY, value);
-    }
-
-    private static void setDefaultValue(final ObjectNode property, final Long value) {
-        property.put(DEFAULT_KEY, value);
-    }
-
-    private static void setDefaultValue(final ObjectNode property, final BigDecimal value) {
-        property.put(DEFAULT_KEY, value);
-    }
-
-    private static void setDefaultValue(final ObjectNode property, final Boolean value) {
-        property.put(DEFAULT_KEY, value);
-    }
-
 }