From 7963dfc159005cbfc436bfeaf714c4c5d4060f22 Mon Sep 17 00:00:00 2001 From: adrianodpdiaz Date: Wed, 8 Apr 2026 22:59:04 +0200 Subject: [PATCH] fix: xml examples autoclose empty tags --- .../json-schema-2020-12-samples/fn/main.js | 27 ++++++++++++++++++- .../plugins/json-schema-5-samples/fn/index.js | 27 ++++++++++++++++++- test/e2e-cypress/e2e/bugs/4943.cy.js | 2 +- test/e2e-cypress/e2e/bugs/6627.cy.js | 2 +- test/e2e-cypress/e2e/features/oas3-xml.cy.js | 4 +-- .../plugins/json-schema-2020-12-samples/fn.js | 10 +++---- .../plugins/json-schema-5-samples/fn/index.js | 2 +- 7 files changed, 62 insertions(+), 12 deletions(-) diff --git a/src/core/plugins/json-schema-2020-12-samples/fn/main.js b/src/core/plugins/json-schema-2020-12-samples/fn/main.js index c5c3d704baf..5663c8af1f3 100644 --- a/src/core/plugins/json-schema-2020-12-samples/fn/main.js +++ b/src/core/plugins/json-schema-2020-12-samples/fn/main.js @@ -536,6 +536,31 @@ export const sampleFromSchemaGeneric = ( return value } +const normalizeXMLContent = (obj) => { + if (Array.isArray(obj)) { + if (obj.length === 0) return null + // If every item in the array is an _attr object (no actual content), collapse + // to a plain object so the xml package renders a self-closing tag with attributes + const nonAttrItems = obj.filter( + (item) => !(item && typeof item === "object" && item._attr) + ) + if (nonAttrItems.length === 0) { + const merged = {} + obj.forEach((item) => Object.assign(merged, item._attr)) + return { _attr: merged } + } + return obj.map(normalizeXMLContent) + } + if (obj !== null && typeof obj === "object") { + const result = {} + for (const key of Object.keys(obj)) { + result[key] = normalizeXMLContent(obj[key]) + } + return result + } + return obj +} + export const createXMLExample = (schema, config, o) => { const json = sampleFromSchemaGeneric(schema, config, o, true) if (!json) { @@ -544,7 +569,7 @@ export const createXMLExample = (schema, config, o) => { if (typeof json === "string") { return json } - return XML(json, { declaration: true, indent: "\t" }) + return XML(normalizeXMLContent(json), { declaration: true, indent: "\t" }) } export const sampleFromSchema = (schema, config, o) => { diff --git a/src/core/plugins/json-schema-5-samples/fn/index.js b/src/core/plugins/json-schema-5-samples/fn/index.js index 44e677c0c4a..3930f41643f 100644 --- a/src/core/plugins/json-schema-5-samples/fn/index.js +++ b/src/core/plugins/json-schema-5-samples/fn/index.js @@ -631,13 +631,38 @@ export const inferSchema = (thing) => { return thing // Hopefully this will have something schema like in it... `type` for example } +const normalizeXMLContent = (obj) => { + if (Array.isArray(obj)) { + if (obj.length === 0) return null + // If every item in the array is an _attr object (no actual content), collapse + // to a plain object so the xml package renders a self-closing tag with attributes + const nonAttrItems = obj.filter( + (item) => !(item && typeof item === "object" && item._attr) + ) + if (nonAttrItems.length === 0) { + const merged = {} + obj.forEach((item) => Object.assign(merged, item._attr)) + return { _attr: merged } + } + return obj.map(normalizeXMLContent) + } + if (obj !== null && typeof obj === "object") { + const result = {} + for (const key of Object.keys(obj)) { + result[key] = normalizeXMLContent(obj[key]) + } + return result + } + return obj +} + export const createXMLExample = (schema, config, o) => { const json = sampleFromSchemaGeneric(schema, config, o, true) if (!json) { return } if(typeof json === "string") { return json } - return XML(json, { declaration: true, indent: "\t" }) + return XML(normalizeXMLContent(json), { declaration: true, indent: "\t" }) } diff --git a/test/e2e-cypress/e2e/bugs/4943.cy.js b/test/e2e-cypress/e2e/bugs/4943.cy.js index 5359754511f..edf7e07019d 100644 --- a/test/e2e-cypress/e2e/bugs/4943.cy.js +++ b/test/e2e-cypress/e2e/bugs/4943.cy.js @@ -15,6 +15,6 @@ describe("#4943: XML example not rendered correctly with oneOf", () => { .get(".try-out__btn") .click() .get(".microlight") - .contains("\n\t") + .contains("") }) }) diff --git a/test/e2e-cypress/e2e/bugs/6627.cy.js b/test/e2e-cypress/e2e/bugs/6627.cy.js index 716b21fa924..a3dd51198cc 100644 --- a/test/e2e-cypress/e2e/bugs/6627.cy.js +++ b/test/e2e-cypress/e2e/bugs/6627.cy.js @@ -1,6 +1,6 @@ describe("#6627: XML example when defined as array", () => { it("should render xml like json", () => { - const expected = "\n\n\t\n\t\n\t\n\t\n" + const expected = "\n\n\t\n\t\n" cy .visit("/?url=/documents/bugs/6627.yaml") .get("#operations-default-get_users") diff --git a/test/e2e-cypress/e2e/features/oas3-xml.cy.js b/test/e2e-cypress/e2e/features/oas3-xml.cy.js index e7f28369536..741441c5fd2 100644 --- a/test/e2e-cypress/e2e/features/oas3-xml.cy.js +++ b/test/e2e-cypress/e2e/features/oas3-xml.cy.js @@ -58,13 +58,13 @@ describe("XML schema rendering examples", () => { .get("#operations-default-post_foo") .click() .get(".response-col_description > .model-example") - .should("contains.text", "") + .should("contains.text", "") }) it("Should render Response example value for mediaType `text/xml`", () => { cy.visit("?url=/documents/features/oas3-xml.json") .get("#operations-default-post_foobar") .click() .get(".response-col_description > .model-example") - .should("contains.text", "") + .should("contains.text", "") }) }) diff --git a/test/unit/core/plugins/json-schema-2020-12-samples/fn.js b/test/unit/core/plugins/json-schema-2020-12-samples/fn.js index 743c3a2b9dc..9a9e955928a 100644 --- a/test/unit/core/plugins/json-schema-2020-12-samples/fn.js +++ b/test/unit/core/plugins/json-schema-2020-12-samples/fn.js @@ -2954,7 +2954,7 @@ describe("createXMLExample", function () { }) it("should use exampleOverride for attr too", () => { const expected = - '\n\n' + '\n\n' const definition = { type: "object", properties: { @@ -3041,8 +3041,8 @@ describe("createXMLExample", function () { } const expected = ` - -` + +` expect(sut(definition)).toEqual(expected) }) @@ -3069,8 +3069,8 @@ describe("createXMLExample", function () { } const expected = ` - -` + +` expect(sut(definition)).toEqual(expected) }) diff --git a/test/unit/core/plugins/json-schema-5-samples/fn/index.js b/test/unit/core/plugins/json-schema-5-samples/fn/index.js index 8e0ab369d3a..c1bb10555b6 100644 --- a/test/unit/core/plugins/json-schema-5-samples/fn/index.js +++ b/test/unit/core/plugins/json-schema-5-samples/fn/index.js @@ -2405,7 +2405,7 @@ describe("createXMLExample", function () { expect(sut(definition, {}, expected)).toEqual(expected) }) it("should use exampleOverride for attr too", () => { - let expected = "\n\n" + let expected = "\n\n" let definition = { type: "object", properties: {