diff --git a/components/seo/XmlToJsonSEO.tsx b/components/seo/XmlToJsonSEO.tsx
new file mode 100644
index 0000000..24aec89
--- /dev/null
+++ b/components/seo/XmlToJsonSEO.tsx
@@ -0,0 +1,30 @@
+import Link from "next/link";
+
+export default function XmlToJsonSEO() {
+ return (
+
+
+ Our free, open-source XML to JSON converter makes it easy to transform your data formats. Built with love for developers.
+
+ Looking for YAML conversion? Check out jsontoyamlconverter.com for JSON to YAML conversions.
+
+
+
+ Why Convert XML to JSON?
+
+ - Modern API Integration: Most REST APIs use JSON
+ - Reduced Data Size: JSON is more compact than XML
+ - JavaScript Native: JSON works seamlessly in web apps
+
+
+
+
+ );
+}
diff --git a/pages/utilities/xml-to-json.tsx b/pages/utilities/xml-to-json.tsx
new file mode 100644
index 0000000..d2eafa9
--- /dev/null
+++ b/pages/utilities/xml-to-json.tsx
@@ -0,0 +1,102 @@
+import { useCallback, useState } from "react";
+import { Textarea } from "@/components/ds/TextareaComponent";
+import PageHeader from "@/components/PageHeader";
+import { Card } from "@/components/ds/CardComponent";
+import { Button } from "@/components/ds/ButtonComponent";
+import { Label } from "@/components/ds/LabelComponent";
+import Header from "@/components/Header";
+import { useCopyToClipboard } from "@/components/hooks/useCopyToClipboard";
+import { CMDK } from "@/components/CMDK";
+import CallToActionGrid from "@/components/CallToActionGrid";
+import XmlToJsonSEO from "@/components/seo/XmlToJsonSEO";
+import Meta from "../../components/Meta";
+
+function xmlToJson(xml: string): unknown {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(xml, "text/xml");
+ const parserError = doc.querySelector("parsererror");
+ if (parserError) throw new Error("Invalid XML");
+
+ function nodeToJson(node: Node): unknown {
+ const obj: Record = {};
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ const element = node as Element;
+ if (element.attributes.length > 0) {
+ obj["@attributes"] = {};
+ for (let i = 0; i < element.attributes.length; i++) {
+ const attr = element.attributes[i];
+ (obj["@attributes"] as Record)[attr.nodeName] = attr.nodeValue || "";
+ }
+ }
+ if (element.hasChildNodes()) {
+ for (let i = 0; i < element.childNodes.length; i++) {
+ const child = element.childNodes[i];
+ if (child.nodeType === Node.TEXT_NODE || child.nodeType === Node.CDATA_SECTION_NODE) {
+ const text = child.textContent?.trim();
+ if (text) {
+ if (element.childNodes.length === 1 && element.attributes.length === 0) return text;
+ obj["#text"] = text;
+ }
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
+ const childName = child.nodeName;
+ const childValue = nodeToJson(child);
+ if (obj[childName] !== undefined) {
+ if (!Array.isArray(obj[childName])) obj[childName] = [obj[childName]];
+ (obj[childName] as unknown[]).push(childValue);
+ } else {
+ obj[childName] = childValue;
+ }
+ }
+ }
+ }
+ if (Object.keys(obj).length === 0) return null;
+ }
+ return obj;
+ }
+ const root = doc.documentElement;
+ return { [root.nodeName]: nodeToJson(root) };
+}
+
+export default function XMLtoJSON() {
+ const [input, setInput] = useState("");
+ const [output, setOutput] = useState("");
+ const { buttonText, handleCopy } = useCopyToClipboard();
+
+ const handleChange = useCallback((event: React.ChangeEvent) => {
+ const { value } = event.currentTarget;
+ setInput(value);
+ if (!value.trim()) { setOutput(""); return; }
+ try {
+ const jsonObject = xmlToJson(value.trim());
+ setOutput(JSON.stringify(jsonObject, null, 2));
+ } catch {
+ setOutput("Invalid XML input");
+ }
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}