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?

+ +
+
+

Related Tools

+ +
+
+ ); +} 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 ( +
+ +
+ +
+ +
+
+ +
+ +