diff --git a/src/pages/tools/json/index.ts b/src/pages/tools/json/index.ts index e1f0c67..6484d1b 100644 --- a/src/pages/tools/json/index.ts +++ b/src/pages/tools/json/index.ts @@ -2,10 +2,12 @@ import { tool as jsonPrettify } from './prettify/meta'; import { tool as jsonMinify } from './minify/meta'; import { tool as jsonStringify } from './stringify/meta'; import { tool as validateJson } from './validateJson/meta'; +import { tool as jsonToXml } from './json-to-xml/meta'; export const jsonTools = [ validateJson, jsonPrettify, jsonMinify, - jsonStringify + jsonStringify, + jsonToXml ]; diff --git a/src/pages/tools/json/json-to-xml/index.tsx b/src/pages/tools/json/json-to-xml/index.tsx new file mode 100644 index 0000000..78cec17 --- /dev/null +++ b/src/pages/tools/json/json-to-xml/index.tsx @@ -0,0 +1,136 @@ +import { useState } from 'react'; +import ToolContent from '@components/ToolContent'; +import ToolTextInput from '@components/input/ToolTextInput'; +import ToolTextResult from '@components/result/ToolTextResult'; +import { convertJsonToXml } from './service'; +import { CardExampleType } from '@components/examples/ToolExamples'; +import { ToolComponentProps } from '@tools/defineTool'; +import { Box } from '@mui/material'; +import CheckboxWithDesc from '@components/options/CheckboxWithDesc'; +import SimpleRadio from '@components/options/SimpleRadio'; + +type InitialValuesType = { + indentationType: 'space' | 'tab' | 'none'; + addMetaTag: boolean; +}; + +const initialValues: InitialValuesType = { + indentationType: 'space', + addMetaTag: false +}; + +const exampleCards: CardExampleType[] = [ + { + title: 'Basic JSON to XML', + description: 'Convert a simple JSON object into an XML format.', + sampleText: ` +{ + "users": [ + { + "name": "John", + "age": 30, + "city": "New York" + }, + { + "name": "Alice", + "age": 25, + "city": "London" + } + ] +}`, + sampleResult: ` +\t +\t\tJohn +\t\t30 +\t\tNew York +\t +\t +\t\tAlice +\t\t25 +\t\tLondon +\t +`, + sampleOptions: { + ...initialValues + } + } +]; + +export default function JsonToXml({ title }: ToolComponentProps) { + const [input, setInput] = useState(''); + const [result, setResult] = useState(''); + + const compute = (values: InitialValuesType, input: string) => { + if (input) { + try { + const xmlResult = convertJsonToXml(input, values); + setResult(xmlResult); + } catch (error) { + setResult( + `Error: ${ + error instanceof Error ? error.message : 'Invalid Json format' + }` + ); + } + } + }; + + return ( + + } + resultComponent={} + getGroups={({ values, updateField }) => [ + { + title: 'Output XML Indentation', + component: ( + + updateField('indentationType', 'space')} + /> + updateField('indentationType', 'tab')} + /> + updateField('indentationType', 'none')} + /> + + ) + }, + { + title: 'XML Meta Information', + component: ( + + updateField('addMetaTag', value)} + title="Add an XML Meta Tag" + description="Add a meta tag at the beginning of the XML output." + /> + + ) + } + ]} + /> + ); +} diff --git a/src/pages/tools/json/json-to-xml/meta.ts b/src/pages/tools/json/json-to-xml/meta.ts new file mode 100644 index 0000000..a0f5f63 --- /dev/null +++ b/src/pages/tools/json/json-to-xml/meta.ts @@ -0,0 +1,12 @@ +import { defineTool } from '@tools/defineTool'; +import { lazy } from 'react'; + +export const tool = defineTool('json', { + name: 'Convert JSON to XML', + path: 'json-to-xml', + icon: 'mdi-light:xml', + description: 'Convert JSON files to XML format with customizable options.', + shortDescription: 'Convert JSON data to XML format', + keywords: ['json', 'xml', 'convert', 'transform', 'parse'], + component: lazy(() => import('./index')) +}); diff --git a/src/pages/tools/json/json-to-xml/service.ts b/src/pages/tools/json/json-to-xml/service.ts new file mode 100644 index 0000000..bbc05c1 --- /dev/null +++ b/src/pages/tools/json/json-to-xml/service.ts @@ -0,0 +1,99 @@ +type JsonToXmlOptions = { + indentationType: 'space' | 'tab' | 'none'; + addMetaTag: boolean; +}; + +export const convertJsonToXml = ( + json: string, + options: JsonToXmlOptions +): string => { + const obj = JSON.parse(json); + return convertObjectToXml(obj, options); +}; + +const getIndentation = (options: JsonToXmlOptions, depth: number): string => { + switch (options.indentationType) { + case 'space': + return ' '.repeat(depth + 1); + case 'tab': + return '\t'.repeat(depth + 1); + case 'none': + default: + return ''; + } +}; + +const convertObjectToXml = ( + obj: any, + options: JsonToXmlOptions, + depth: number = 0 +): string => { + let xml = ''; + + const newline = options.indentationType === 'none' ? '' : '\n'; + + if (depth === 0) { + if (options.addMetaTag) { + xml += '' + newline; + } + xml += '' + newline; + } + + for (const key in obj) { + const value = obj[key]; + const keyString = isNaN(Number(key)) ? key : `row-${key}`; + + // Handle null values + if (value === null) { + xml += `${getIndentation( + options, + depth + )}<${keyString}>${newline}`; + continue; + } + + // Handle arrays + if (Array.isArray(value)) { + value.forEach((item) => { + xml += `${getIndentation(options, depth)}<${keyString}>`; + if (item === null) { + xml += `${newline}`; + } else if (typeof item === 'object') { + xml += `${newline}${convertObjectToXml( + item, + options, + depth + 1 + )}${getIndentation(options, depth)}`; + xml += `${newline}`; + } else { + xml += `${escapeXml(String(item))}${newline}`; + } + }); + continue; + } + + // Handle objects + if (typeof value === 'object') { + xml += `${getIndentation(options, depth)}<${keyString}>${newline}`; + xml += convertObjectToXml(value, options, depth + 1); + xml += `${getIndentation(options, depth)}${newline}`; + continue; + } + + // Handle primitive values (string, number, boolean, etc.) + xml += `${getIndentation(options, depth)}<${keyString}>${escapeXml( + String(value) + )}${newline}`; + } + + return depth === 0 ? `${xml}` : xml; +}; + +const escapeXml = (str: string): string => { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +};