diff --git a/.changeset/early-singers-train.md b/.changeset/early-singers-train.md
new file mode 100644
index 000000000..edec2637e
--- /dev/null
+++ b/.changeset/early-singers-train.md
@@ -0,0 +1,5 @@
+---
+'@gitbook/react-openapi': patch
+---
+
+Fix XML in code sample
diff --git a/packages/react-openapi/src/code-samples.test.ts b/packages/react-openapi/src/code-samples.test.ts
index 4945f9fc9..adcb3922a 100644
--- a/packages/react-openapi/src/code-samples.test.ts
+++ b/packages/react-openapi/src/code-samples.test.ts
@@ -110,6 +110,23 @@ describe('curL code sample generator', () => {
);
});
+ it('should convert json to xml body properly', () => {
+ const input: CodeSampleInput = {
+ method: 'GET',
+ url: 'https://example.com/path',
+ headers: {
+ 'Content-Type': 'application/xml',
+ },
+ body: '{ "key": "value" }',
+ };
+
+ const output = generator?.generate(input);
+
+ expect(output).toBe(
+ "curl -L \\\n --url 'https://example.com/path' \\\n --header 'Content-Type: application/xml' \\\n --data-binary $'\n value\n '"
+ );
+ });
+
it('should format application/graphql body properly', () => {
const input: CodeSampleInput = {
method: 'GET',
@@ -258,6 +275,23 @@ describe('javascript code sample generator', () => {
);
});
+ it('should convert json to xml body properly', () => {
+ const input: CodeSampleInput = {
+ method: 'GET',
+ url: 'https://example.com/path',
+ headers: {
+ 'Content-Type': 'application/xml',
+ },
+ body: '{ "key": "value" }',
+ };
+
+ const output = generator?.generate(input);
+
+ expect(output).toBe(
+ 'const xml = `\n \n value\n`;\n\nconst response = await fetch(\'https://example.com/path\', {\n method: \'GET\',\n headers: {\n "Content-Type": "application/xml"\n },\n body: xml\n});\n\nconst data = await response.json();'
+ );
+ });
+
it('should format application/graphql body properly', () => {
const input: CodeSampleInput = {
method: 'GET',
@@ -406,6 +440,23 @@ describe('python code sample generator', () => {
);
});
+ it('should convert json to xml body properly', () => {
+ const input: CodeSampleInput = {
+ method: 'GET',
+ url: 'https://example.com/path',
+ headers: {
+ 'Content-Type': 'application/xml',
+ },
+ body: '{ "key": "value" }',
+ };
+
+ const output = generator?.generate(input);
+
+ expect(output).toBe(
+ 'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/xml"},\n data="\\nvalue\\n"\n)\n\ndata = response.json()'
+ );
+ });
+
it('should format application/graphql body properly', () => {
const input: CodeSampleInput = {
method: 'GET',
@@ -514,7 +565,7 @@ describe('http code sample generator', () => {
const output = generator?.generate(input);
expect(output).toBe(
- 'GET /path HTTP/1.1\nHost: example.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 15\nAccept: */*\n\n"key=value"'
+ 'GET /path HTTP/1.1\nHost: example.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 15\nAccept: */*\n\n"key=\'value\'"'
);
});
@@ -554,6 +605,23 @@ describe('http code sample generator', () => {
);
});
+ it('should convert json to xml body properly', () => {
+ const input: CodeSampleInput = {
+ method: 'GET',
+ url: 'https://example.com/path',
+ headers: {
+ 'Content-Type': 'application/xml',
+ },
+ body: '{ "key": "value" }',
+ };
+
+ const output = generator?.generate(input);
+
+ expect(output).toBe(
+ 'GET /path HTTP/1.1\nHost: example.com\nContent-Type: application/xml\nContent-Length: 24\nAccept: */*\n\n"\nvalue\n"'
+ );
+ });
+
it('should format application/graphql body properly', () => {
const input: CodeSampleInput = {
method: 'GET',
diff --git a/packages/react-openapi/src/code-samples.ts b/packages/react-openapi/src/code-samples.ts
index 24bd1ca72..44d29b95b 100644
--- a/packages/react-openapi/src/code-samples.ts
+++ b/packages/react-openapi/src/code-samples.ts
@@ -8,6 +8,7 @@ import {
isText,
isXML,
} from './contentTypeChecks';
+import { json2xml } from './json2xml';
import { stringifyOpenAPI } from './stringifyOpenAPI';
export interface CodeSampleInput {
@@ -238,7 +239,10 @@ const BodyGenerators = {
: String(body);
} else if (isText(contentType)) {
body = `--data '${String(body).replace(/"/g, '')}'`;
- } else if (isXML(contentType) || isCSV(contentType)) {
+ } else if (isXML(contentType)) {
+ // Convert to XML and ensure proper formatting
+ body = `--data-binary $'${convertBodyToXML(body)}'`;
+ } else if (isCSV(contentType)) {
// We use --data-binary to avoid cURL converting newlines to \r\n
body = `--data-binary $'${stringifyOpenAPI(body).replace(/"/g, '').replace(/\\n/g, '\n')}'`;
} else if (isGraphQL(contentType)) {
@@ -312,7 +316,9 @@ const BodyGenerators = {
body = 'formData';
} else if (isXML(contentType)) {
code += 'const xml = `\n';
- code += indent(String(body), 4);
+
+ // Convert JSON to XML if needed
+ code += indent(convertBodyToXML(body), 4);
code += '`;\n\n';
body = 'xml';
} else if (isText(contentType)) {
@@ -346,6 +352,11 @@ const BodyGenerators = {
body = 'files';
}
+ if (isXML(contentType)) {
+ // Convert JSON to XML if needed
+ body = convertBodyToXML(body);
+ }
+
return { body, code, headers };
},
getHTTPBody: (body: any, headers?: Record) => {
@@ -358,23 +369,48 @@ const BodyGenerators = {
formUrlEncoded: () => {
const encoded = isPlainObject(body)
? Object.entries(body)
- .map(([key, value]) => `${key}=${String(value)}`)
+ .map(([key, value]) => `${key}=${stringifyOpenAPI(value)}`)
.join('&')
- : String(body);
- return `"${encoded}"`;
+ : stringifyOpenAPI(body);
+ return `"${encoded.replace(/"/g, "'")}"`;
},
text: () => `"${String(body)}"`,
- xmlOrCsv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
+ xml: () => {
+ // Convert JSON to XML if needed
+ return `"${convertBodyToXML(body)}"`;
+ },
+ csv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
default: () => `${stringifyOpenAPI(body, null, 2)}`,
};
if (isPDF(contentType)) return typeHandlers.pdf();
if (isFormUrlEncoded(contentType)) return typeHandlers.formUrlEncoded();
if (isText(contentType)) return typeHandlers.text();
- if (isXML(contentType) || isCSV(contentType)) {
- return typeHandlers.xmlOrCsv();
- }
+ if (isXML(contentType)) return typeHandlers.xml();
+ if (isCSV(contentType)) return typeHandlers.csv();
return typeHandlers.default();
},
};
+
+/**
+ * Converts a body to XML format
+ */
+function convertBodyToXML(body: any): string {
+ // If body is already a string and looks like XML, return it as is
+ if (typeof body === 'string' && body.trim().startsWith('<')) {
+ return body;
+ }
+
+ // If body is not an object, try to parse it as JSON
+ if (typeof body !== 'object' || body === null) {
+ try {
+ body = JSON.parse(body);
+ } catch {
+ // If parsing fails, return the original body
+ return body;
+ }
+ }
+
+ return json2xml(body).replace(/"/g, '').replace(/\\n/g, '\n').replace(/\\t/g, '\t');
+}