Skip to content

Incorrect header hierarchy for nested properties in allOf with anyOf #678

@lmezler-enbw

Description

@lmezler-enbw

Description

When using allOf with multiple schemas where one contains an anyOf, the generated markdown documentation has incorrect header hierarchy for the property detail sections. Specifically, properties that are defined in separate allOf items appear with headers at the wrong level.

Expected Behavior

For a schema structure like:

# Example Schema (level 1)
## tests (level 2)
### tests.docker (level 3)
#### Option 1: testScript (level 4)
#### Option 2: testSteps (level 4)
#### preTestSteps (level 4)

All detail sections under tests.docker should be at level 4 (####) since they're nested under a level 3 header (###).

Actual Behavior

The generated markdown has inconsistent header levels:

# Example Schema (level 1)
## tests (level 2)
### tests.docker (level 3)
   Option 1: testScript (no header - inline)
   Option 2: testSteps (no header - inline)
## Option 2]: testSteps[]: Test Steps (level 2 ❌ should be level 4)
### 1.preTestSteps[]: Pre-test Steps (level 3 ❌ should be level 4)

The detail sections for testSteps and preTestSteps are promoted to level 2 and 3 respectively, breaking the document hierarchy.

Minimal Reproduction

See also:
jsonschema2md-hirachie-test.zip

Input Schema (schema.json):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Example Schema",
  "type": "object",
  "properties": {
    "tests": {
      "title": "Tests Configuration",
      "description": "Configure test execution",
      "type": "object",
      "properties": {
        "docker": {
          "title": "Docker Tests",
          "description": "Run Docker-based tests",
          "type": "object",
          "allOf": [
            {
              "title": "test-options",
              "description": "Test execution options",
              "anyOf": [
                {
                  "title": "script",
                  "properties": {
                    "testScript": {
                      "title": "Test Script",
                      "description": "Script to execute for testing",
                      "type": "string"
                    }
                  },
                  "required": ["testScript"]
                },
                {
                  "title": "stepList",
                  "properties": {
                    "testSteps": {
                      "title": "Test Steps",
                      "description": "List of test steps to execute",
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  },
                  "required": ["testSteps"]
                }
              ]
            },
            {
              "properties": {
                "preTestSteps": {
                  "title": "Pre-test Steps",
                  "description": "Steps to execute before running tests",
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          ]
        }
      }
    }
  }
}

Code to generate markdown:

const jsonschema2mk = require("jsonschema2mk");
const fs = require("fs");

const schema = JSON.parse(fs.readFileSync("schema.json", "utf8"));

const jsm = new jsonschema2mk({
  schema: undefined,
  level: undefined,
  extension: ["table-format-2", "yaml-examples"],
  plugin: undefined,
  partials: undefined,
});

const markdown = jsm.convert(schema);
fs.writeFileSync("output.md", markdown);

Problematic output (lines 90 and 107):

<a name="testsdocker"></a>
### tests\.docker: Docker Tests

Run Docker-based tests


**Option 1 (optional):**
**Properties**
...

<a name="option2teststeps"></a>
## Option 2\]: testSteps\[\]: Test Steps    👈 Should be #### not ##

List of test steps to execute

<a name="1preteststeps"></a>
### 1\.preTestSteps\[\]: Pre\-test Steps    👈 Should be #### not ###

Steps to execute before running tests

Environment

  • jsonschema2md version: 8,0,7
  • Node.js version: v22.20.0

Workaround

As a temporary workaround, we:

  1. Reorder the allOf items to put simpler properties before complex ones (with anyOf/oneOf)
  2. Post-process the generated markdown with regex replacements to fix header levels

Impact

This breaks the logical document structure and makes navigation difficult, especially in documentation systems that rely on proper heading hierarchy for table of contents generation. Thi sis a simplified example and in complex documents it gets unreadable.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions