Skip to content
블로그로 돌아가기
튜토리얼

XML을 JSON으로 온라인 변환: 규칙, 함정, 코드 예제

XML을 JSON으로 올바르게 변환하는 방법. 속성·배열·네임스페이스 매핑, 값이 문자열로 유지되는 이유, JavaScript·Python·브라우저 코드 예제를 함께 다룹니다.

13분 소요

XML을 JSON으로 변환: 규칙, 함정, 코드 예제

SOAP 엔드포인트나 RSS 피드, 또는 sitemap.xml에서 응답을 받아 보면 그것이 XML입니다. 반면 여러분의 스택은 JSON 중심입니다. 프런트엔드는 자바스크립트(JavaScript), 중간 계층은 REST, 바닥에는 문서 저장소가 있습니다. 그래서 XML을 JSON으로 변환해야 하고, 한 줄짜리 코드면 될 것이라 기대하며 파서에 손을 뻗습니다.

대개는 정말 한 줄로 끝납니다. 출력 결과를 확인하기 전까지는요. 배열이 나오리라 기대했는데 단일 객체가 돌아옵니다. id 속성이 사라집니다. 01234 같은 우편번호가 숫자 1234로 돌아옵니다. 이 가운데 어느 것도 파서의 버그가 아닙니다. 서로 들어맞지 않는 두 데이터 모델을 매핑하다 보니 생긴 결과입니다. XML을 JSON으로 안정적으로 변환하려면 그 간극을 잇는 규칙을 알아야 합니다.

이 가이드에서는 그러한 규칙이 존재하는 이유, 변환을 수행하는 네 가지 방법(브라우저, 자바스크립트, Python, CLI), 주요 라이브러리가 공통으로 따르는 @_#text 규칙, 소리 없이 데이터를 잃게 만드는 다섯 가지 함정, 그리고 깔끔한 왕복 변환을 위해 JSON을 다시 XML로 되돌리는 방법을 다룹니다. 예제는 실제로 동작하므로 그대로 실행하면 됩니다. Node, Python, 셸에 붙여넣으면 주석에 적힌 출력이 그대로 나옵니다.

XML-JSON 변환에 단순 재포맷이 아니라 규칙이 필요한 이유

XML과 JSON은 겉보기에 비슷합니다. 둘 다 이름이 붙고 중첩된 데이터의 트리입니다. 하지만 그 밑바탕의 모델은 중요한 지점에서 갈라집니다. XML 요소는 속성을 가질 수 있고, 혼합 콘텐츠(자식 요소 사이에 텍스트가 섞여 있는 형태)를 담을 수 있으며, 네임스페이스 아래에 존재할 수 있습니다. JSON에는 그런 개념이 하나도 없습니다. JSON에는 객체, 배열, 그리고 네 가지 스칼라 타입만 있습니다. 그래서 한쪽을 다른 쪽으로 옮기는 일은 단순한 재포맷이 아닙니다. 한쪽 문법에는 있지만 다른 쪽으로는 받아 적을 수 없는 단어가 있는, 두 문법 사이의 번역에 가깝습니다.

무엇이든 변환하기 전에, 원본이 실제로 유효한지 먼저 확인하는 것이 좋습니다. 이스케이프되지 않은 & 하나, 또는 짝이 맞지 않는 태그 하나만 있어도 파서에서 거부되므로, 먼저 입력을 XML 포맷터에 통과시켜 적격성(well-formedness)을 확인하면 혼란스러운 오류를 한 차례 겪지 않아도 됩니다.

두 모델이 갈라지는 지점은 다음과 같습니다.

항목XMLJSON
노드 타입요소, 속성, 텍스트, 혼합 콘텐츠객체, 배열, 문자열, 숫자, 불리언, null
루트 제약정확히 하나의 루트 요소 필요루트 제약 없음
속성있음 (id="P01")없음 (@_ 규칙 필요)
반복 요소같은 이름의 형제 요소 허용객체 키는 중복 불가 (배열 규칙 필요)
타입 시스템텍스트는 타입이 없음 — 모두 문자열네이티브 타입
네임스페이스있음 (xmlns)없음

모델이 서로 일치하지 않으므로, 모든 XML-JSON 변환은 무손실 재포맷이 아니라 규칙에 따라 이뤄집니다. 다행히 이 규칙들은 제멋대로 정해진 것이 아닙니다. fast-xml-parser(Node.js), xmltodict(Python), JAXB(Java)가 모두 같은 두 마커로 수렴했습니다. 속성에는 @_, 혼합 콘텐츠 텍스트에는 #text입니다. 한 번 익혀 두면 어느 런타임에서나 그대로 통합니다. 데이터 형태의 불일치는 다른 변환에서도 같은 이유로 나타나는데, CSV-JSON 변환 가이드의 타입 추론 문제가 그 예입니다.

XML을 JSON으로 변환하는 방법: 4가지

상황에 맞는 방법을 고르세요. 가볍게 한 번 붙여넣는 경우, Node 서비스, Python 파이프라인, 또는 CI의 셸 스크립트 가운데 무엇이든 됩니다.

방법 1 — 브라우저 기반 도구 (설정 불필요, 프라이버시 우선)

일회성 변환이거나 아무 웹사이트에나 붙여넣고 싶지 않은 XML이라면, 브라우저 내 변환기가 가장 빠른 길입니다. XML to JSON 변환기에 XML을 붙여넣으면 JSON이 즉시 나타납니다. 설치도, 계정도, 업로드도 없습니다. 모든 처리가 브라우저의 자바스크립트 엔진 안에서 일어나므로 데이터가 기기를 떠나지 않습니다.

이 마지막 사항은 생각보다 중요합니다. SOAP 봉투(envelope)는 WS-Security 토큰을 싣고, 내부 설정에는 연결 문자열이 들어 있으며, 내보내기 파일에는 고객 레코드가 담겨 있습니다. 아무것도 전송되지 않으니, 이 도구는 자격 증명이나 민감한 페이로드가 담긴 XML에도 안심하고 쓸 수 있습니다. 직접 확인해 봐도 됩니다. 네트워크 탭을 열고 변환하는 동안 요청이 하나도 발생하지 않는지 지켜보세요.

방법 2 — JavaScript / Node.js (fast-xml-parser)

Node에서는 fast-xml-parser가 표준 선택지입니다. 다만 기본값에 놀랄 수 있습니다. 속성이 무시되고 값에 타입이 강제 변환되기 때문입니다. 그래서 아래 옵션이 충실한 변환을 위해 실제로 필요한 설정입니다.

// fast-xml-parser로 Node.js에서 XML을 JSON으로 변환
import { XMLParser } from 'fast-xml-parser';

const xml = `<catalog>
  <product id="P01">
    <name>Wireless Headphones</name>
    <price currency="USD">79.99</price>
  </product>
</catalog>`;

const parser = new XMLParser({
  ignoreAttributes: false,    // 속성 유지 (기본값은 속성을 버림!)
  attributeNamePrefix: '@_',  // 속성은 @_ 접두 키가 됨
  textNodeName: '#text',      // 혼합 콘텐츠 텍스트는 #text 아래로
  parseAttributeValue: false, // 속성에 타입 강제 변환 안 함
  parseTagValue: false,       // 요소 텍스트에 타입 강제 변환 안 함
});

const result = parser.parse(xml);
console.log(JSON.stringify(result, null, 2));
// {
//   "catalog": {
//     "product": {
//       "@_id": "P01",
//       "name": "Wireless Headphones",
//       "price": {
//         "@_currency": "USD",
//         "#text": "79.99"
//       }
//     }
//   }
// }

사람들이 잊기 쉬운 두 설정은 ignoreAttributes: falseparseTagValue: false입니다. 앞의 것은 idcurrency 속성을 지켜 주고, 뒤의 것은 파서가 "79.99"를 부동소수점으로, "01234"1234로 바꾸지 못하게 막아 줍니다. 문자열 보존이 왜 안전한 기본값인지는 함정 섹션에서 다시 다루겠습니다.

브라우저에서 의존성을 전혀 두고 싶지 않다면, 네이티브 DOMParser가 파싱을 대신해 주고, DOM 순회는 직접 작성하면 됩니다.

// DOMParser를 사용해 브라우저에서 의존성 없이 XML을 JSON으로 변환
function xmlToJson(node) {
  // 텍스트만 있는 요소 → 문자열 값
  const children = Array.from(node.children);
  if (children.length === 0 && node.attributes.length === 0) {
    return node.textContent.trim();
  }

  const obj = {};
  // 속성 → @_ 접두
  for (const attr of node.attributes) {
    obj['@_' + attr.name] = attr.value;
  }
  // 속성과 텍스트를 모두 가진 요소 → #text
  if (children.length === 0) {
    obj['#text'] = node.textContent.trim();
    return obj;
  }
  // 자식으로 재귀, 같은 이름의 형제는 배열로 수집
  for (const child of children) {
    const value = xmlToJson(child);
    if (obj[child.tagName] === undefined) {
      obj[child.tagName] = value;
    } else {
      if (!Array.isArray(obj[child.tagName])) obj[child.tagName] = [obj[child.tagName]];
      obj[child.tagName].push(value);
    }
  }
  return obj;
}

const doc = new DOMParser().parseFromString(
  '<catalog><product id="P01"><name>Wireless Headphones</name></product></catalog>',
  'text/xml'
);
const json = { [doc.documentElement.tagName]: xmlToJson(doc.documentElement) };
console.log(JSON.stringify(json, null, 2));
// { "catalog": { "product": { "@_id": "P01", "name": "Wireless Headphones" } } }

DOMParser는 XML 1.0을 준수하고, CDATA와 엔터티 참조를 처리하며, 적격성 오류를 보고합니다. 모두 패키지 설치 없이 가능합니다. 그 대가로 위에 보인 배열 수집 규칙을 포함해 순회 로직을 직접 책임져야 합니다.

방법 3 — Python (xmltodict)

Python에서는 xmltodict가 전체 작업을 짧은 파이프라인으로 압축합니다. 기본적으로 속성 접두로 @를, 혼합 콘텐츠에 #text를 사용합니다.

# xmltodict로 Python에서 XML을 JSON으로 변환
import json
import xmltodict

xml = """<catalog>
  <product id="P01">
    <name>Wireless Headphones</name>
    <price currency="USD">79.99</price>
  </product>
</catalog>"""

data = xmltodict.parse(xml)
print(json.dumps(data, indent=2))
# {
#   "catalog": {
#     "product": {
#       "@id": "P01",
#       "name": "Wireless Headphones",
#       "price": {
#         "@currency": "USD",
#         "#text": "79.99"
#       }
#     }
#   }
# }

기본적으로 xmltodict는 모든 값을 문자열로 유지하는데, 이것이 바로 우리가 원하는 동작입니다. 미리 알아 둘 만한 옵션 하나는 force_list로, 단일-대-다수 배열 문제를 코드에 도달하기 전에 바로잡아 줍니다.

# force_list는 <product>가 하나뿐일 때도 항상 리스트가 되도록 보장한다
data = xmltodict.parse(xml, force_list={'product'})
products = data['catalog']['product']  # 이제 항상 리스트
for p in products:
    print(p['name'])

force_list 없이는 <product> 하나는 dict를, 둘은 list를 만들어 냅니다. 그래서 항목이 하나뿐인 경우에 루프가 깨집니다. 이것이 아래에서 다룰 함정 1번입니다.

방법 4 — CLI (yq / Python 한 줄 명령)

셸 스크립트와 CI 파이프라인이라면, 두 가지 한 줄 명령이 대부분의 경우를 해결합니다. Mike Farah의 yq는 XML을 읽어 JSON을 곧장 내보냅니다.

# yq 사용 (Mike Farah의 Go 버전)
yq -p=xml -o=json '.' input.xml

# stdin에서 파이프로 입력
cat sitemap.xml | yq -p=xml -o=json '.'

xmltodict가 이미 환경에 설치되어 있다면, 추가 바이너리가 필요 없는 Python 한 줄 명령을 쓸 수 있습니다.

python3 -c "import sys, xmltodict, json; print(json.dumps(xmltodict.parse(sys.stdin.read()), indent=2))" < input.xml

둘 다 stdin에서 스트리밍하므로 파이프라인에 그대로 끼워 넣을 수 있습니다. 스크립트 도중 API 응답을 변환하거나, 빌드 단계에서 여러 파일을 한꺼번에 정규화할 때 유용합니다.

@_ 속성과 #text 규칙 풀어 보기

대부분의 변환기 페이지는 정작 중요한 부분을 건너뜁니다. 바로 그 낯선 @_#text 키가 무슨 의미이고 왜 존재하느냐입니다. 이 둘을 제대로 이해하면 출력이 더 이상 제멋대로처럼 보이지 않습니다.

속성은 @_ 접두 키로 매핑됩니다. 속성에는 JSON 대응물이 없습니다. 객체 안에는 “이 객체에 대한 메타데이터”를 자식과 구분해 담을 자리가 없기 때문입니다. 그래서 규칙은 속성에 @_를 앞에 붙인 키를 부여하는 것입니다.

<user id="42" role="admin"/>
→ { "user": { "@_id": "42", "@_role": "admin" } }

왜 하필 @_일까요? 유효한 XML 요소 이름은 @로 시작할 수 없으므로, 이 접두는 실제 자식 요소 키와 충돌할 일이 없습니다. 사실상 예약된 네임스페이스 역할을 하는 셈입니다. (xmltodict는 맨 @만 쓰고, fast-xml-parser는 기본값으로 @_를 씁니다. 원리는 같습니다.)

혼합 콘텐츠는 #text로 매핑됩니다. 요소가 속성과 텍스트 값을 모두 가질 때, 텍스트는 속성 키들과 나란히 자리할 곳이 필요합니다. 그것이 #text입니다.

<price currency="USD">29.99</price>
→ { "price": { "@_currency": "USD", "#text": "29.99" } }

일반 텍스트 요소는 직접 문자열 값이 됩니다. 속성도 자식도 없이 텍스트만 있다면 #text로 한 단계 우회할 필요가 없습니다. <name>Alice</name>"name": "Alice"가 됩니다. #text 키는 속성 때문에 요소 값이 객체가 되어야 할 때만 나타납니다.

이 비대칭이 미묘한 버그를 낳습니다. 같은 요소 이름이라도 그 인스턴스가 속성을 가졌는지에 따라, 어떤 문서에서는 일반 문자열을, 어떤 문서에서는 @_/#text 객체를 만들어 냅니다. currency 속성이 없는 <price>는 문자열 "29.99"이고, 같은 <price currency="USD">{ "@_currency": "USD", "#text": "29.99" }입니다. node.price를 곧바로 읽는 코드는 한쪽 형태에서는 동작하다가 다른 형태에서는 소리 없이 깨집니다. 그래서 접근자에서 타입을 먼저 확인하는 편이 안전합니다. const amount = typeof node.price === 'object' ? node.price['#text'] : node.price;처럼요.

CDATA는 일반 텍스트 콘텐츠가 됩니다. <![CDATA[if (a < b) return;]]> 섹션은 이스케이프 수단일 뿐이므로, 구분자는 떨어져 나가고 안쪽 텍스트만 남습니다. 즉 "if (a < b) return;"입니다. CDATA라는 표시는 JSON에 전혀 남지 않습니다.

출력이 나오면, JSON 포맷터에 붙여넣어 JSON 출력의 유효성을 검사하고, 코드에 연결하기 전에 구조가 소비 측에서 기대하는 형태와 일치하는지 확인하세요.

XML-JSON 변환의 5가지 함정과 회피법

아래 다섯 가지는 코드 리뷰를 무사히 통과한 뒤 운영 환경에서야 터지는 실패입니다. 하나하나가 이 가이드 첫머리에서 다룬 모델 불일치로 거슬러 올라갑니다.

1. 배열의 모호함 (하나 대 여럿). <item>이 하나면 객체가 되고, 둘 이상이면 배열이 됩니다. JSON 형태는 그 특정 문서에 형제가 몇 개 들어 있었느냐에 좌우됩니다. result.items.item.forEach(...) 같은 소비 측 코드는 항목이 세 개인 픽스처로 테스트할 때는 잘 동작하다가, 레코드에 항목이 정확히 하나만 있는 운영 환경에서 TypeError: not a function을 던집니다.

// <book> 형제 둘 → 배열
// <library><book>A</book><book>B</book></library>
// → { "library": { "book": ["A", "B"] } }

// <book> 하나 → 객체, 배열이 아님
// <library><book>A</book></library>
// → { "library": { "book": "A" } }

// 두 경우가 동일하게 동작하도록 정규화
const books = [].concat(result.library?.book ?? []);
books.forEach(b => console.log(b)); // 0개, 1개, 여러 개 모두 안전

[].concat(x ?? []) 관용구는 외워 둘 만합니다. 값이 없으면 []가 되고, 단일 객체는 [object]가 되며, 이미 배열이면 그대로 통과합니다. Python에서는 xmltodict.parse()force_list={'book'}을 넘기면 값이 항상 리스트가 되므로, 정규화를 통째로 건너뛸 수 있습니다.

2. 속성이 조용히 사라짐. 여러 라이브러리가 기본적으로 속성을 무시합니다. fast-xml-parserignoreAttributes: false를 설정하기 전까지 정확히 이렇게 동작합니다. 변환은 잘된 것처럼 보이고, JSON도 멀쩡히 파싱되는데, 여러분의 id, currency, status 값은 그냥 사라져 버립니다. 기본값을 믿지 말고 항상 이 플래그를 명시적으로 설정하세요.

3. 네임스페이스 평탄화. xmlns 선언은 평범한 @_xmlns 키가 되고, <soap:Body>의 접두는 문자열 키 "soap:Body"의 일부로만 남습니다. 두 접두가 같은 URI에 묶일 수 있다는 의미론적 정보는 사라집니다.

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>...</soap:Body>
</soap:Envelope>
→ {
    "soap:Envelope": {
      "@_xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
      "soap:Body": "..."
    }
  }

이렇게 되면 접두 soap:는 키 이름 속의 글자일 뿐, 그것이 네임스페이스라는 사실은 어디에도 남지 않습니다. 서로 다른 네임스페이스의 두 요소가 같은 로컬 이름을 쓰면 충돌이 날 수 있습니다. 정밀한 네임스페이스 처리가 요건에 포함된다면, 데이터를 네임스페이스 인식 파서 안에 그대로 두고 JSON으로 평탄화하지 마세요.

4. 타입을 강제 변환하지 않으며, 그게 맞습니다. <zip>01234</zip>는 절대 1234가 되어선 안 됩니다. 계정 코드, 우편번호, 자릿수를 채운 식별자, 정밀도가 중요한 소수는 소리 없는 강제 변환에서 모두 망가집니다. 좋은 변환기는 모든 값을 문자열로 두고, 강제 변환은 여러분이 직접 의도적으로 하도록 맡깁니다.

// 암묵적 강제 변환에 의존하지 말 것
if (config.timeout > 25) { /* 위태로움: "30" > 25 가 우연히 동작 */ }

// 타입을 아는 곳에서만 명시적으로 강제 변환
if (parseInt(config.timeout, 10) > 25) { /* 안전 */ }

5. 손실: 주석, 처리 명령, 혼합 콘텐츠 순서. XML 주석(<!-- ... -->)과 처리 명령(<?xml-stylesheet ?>)은 JSON에 들어갈 자리가 없어 버려집니다. 자식 요소 사이에 섞인 텍스트의 상대적 순서도 왕복 과정에서 흐트러질 수 있습니다. 원본 문서를 한 바이트도 빠짐없이 그대로 다시 내보내야 한다면, 아예 변환하지 마세요. 데이터 모델을 건드리지 않고 재포맷하거나 최소화하려면 XML 포맷터를 쓰면 됩니다.

JSON을 다시 XML로 변환하기 (왕복)

반대 방향에는 따로 신경 쓸 점이 있습니다. JSON에는 루트 요소 규칙이 없는데 XML은 루트를 정확히 하나 요구하기 때문입니다. 짝을 이루는 JSON to XML 변환기는 같은 @_/#text 규칙을 거꾸로 적용하므로, JSON → XML → JSON 과정에서 속성, 텍스트, 구조가 그대로 보존됩니다.

눈여겨볼 지점은 루트 정규화입니다. 변환기는 단일 루트 요건을 네 가지 규칙으로 해결합니다.

  • 단일 키 객체 → 그 키가 루트가 됩니다: { "config": {...} }<config>...</config>.
  • 다중 키 객체<root>로 감쌉니다: { "a": 1, "b": 2 }<root><a>1</a><b>2</b></root>.
  • 최상위 배열<root><item>...</item></root>로 감싸며, <item>을 고정 폴백 이름으로 사용합니다.
  • 원시 값<root>value</root>.

나머지는 모두 정방향을 그대로 뒤집은 것입니다. @_ 키는 속성이 되고, #text는 텍스트 콘텐츠가 되며, 키 아래의 JSON 배열은 같은 이름의 형제 요소로 풀려 나옵니다. 키 이름은 그대로 재사용되며 단수형으로 바뀌지 않습니다.

// fast-xml-parser로 Node.js에서 JSON을 XML로 변환
import { XMLBuilder } from 'fast-xml-parser';

const data = {
  catalog: {
    product: {
      '@_id': 'P01',
      name: 'Wireless Headphones',
      price: { '@_currency': 'USD', '#text': '79.99' },
    },
  },
};

const builder = new XMLBuilder({
  attributeNamePrefix: '@_', // @_ 키는 속성이 됨
  textNodeName: '#text',     // #text 키는 텍스트 콘텐츠가 됨
  ignoreAttributes: false,   // @_ 키 처리
  format: true,              // 예쁘게 출력
});

console.log(builder.build(data));
// <catalog>
//   <product id="P01">
//     <name>Wireless Headphones</name>
//     <price currency="USD">79.99</price>
//   </product>
// </catalog>

빌더가 알아서 챙겨 주는 세부 사항이 하나 있습니다. 텍스트와 속성 값 안의 특수 문자(<, >, &, ")를 엔터티 참조로 이스케이프해 주므로, 출력이 계속 적격 상태로 유지됩니다.

FAQ

XML 속성은 JSON으로 어떻게 매핑되나요?

속성은 @_를 앞에 붙인 키가 되므로 id="42""@_id": "42"로 바뀝니다. 이것이 fast-xml-parserxmltodict가 공유하는 규칙이며, 유효한 요소 이름은 @로 시작할 수 없으므로 이 접두는 요소 이름과 절대 충돌하지 않습니다.

XML을 JSON으로 변환할 때 숫자가 문자열로 유지되는 이유는 무엇인가요?

변환기가 타입 강제 변환을 전혀 하지 않기 때문입니다. 012341234로 강제하면 우편번호, 계정 번호, 자릿수를 채운 ID에서 의미 있는 선행 0이 사라집니다. 모든 값을 문자열로 유지하는 것이 안전한 기본값이며, 타입을 아는 하류 단계에서 의도적으로 강제 변환하세요.

XML을 JSON으로 변환하면 무손실인가요?

아닙니다. 주석과 처리 명령은 버려지고, 네임스페이스 의미론은 부분적으로만 보존되며, 혼합 콘텐츠 순서는 왕복하지 못할 수 있습니다. 한 바이트도 빠짐없이 보존해야 한다면, JSON으로 변환하는 대신 XML 포맷터를 사용해 XML을 재포맷하세요.

반복되는 XML 요소는 JSON에서 어떻게 처리되나요?

같은 이름의 자식이 하나면 객체가 되고, 둘 이상이면 배열이 됩니다. 형태가 형제 개수에 좌우되므로, 소비 측 코드는 항상 배열로 정규화해 항목이 하나인 경우와 여럿인 경우를 모두 깨지지 않게 처리해야 합니다.

XML 네임스페이스는 JSON으로 변환될 때 어떻게 되나요?

xmlns 선언은 평범한 @_xmlns 키가 되고, 접두는 "soap:Body"처럼 요소 이름 문자열 안에 남습니다. 접두를 URI에 묶는 의미론적 연결은 해석되지 않으므로, 서로 다른 네임스페이스가 한데 평탄화될 수 있습니다.

JSON을 다시 XML로 어떻게 변환하나요?

짝을 이루는 JSON to XML 변환기를 사용하세요. 동일한 @_#text 규칙을 역방향으로 적용하므로, 속성, 텍스트 콘텐츠, 배열이 대칭으로 다시 매핑됩니다. 바로 그 대칭성 덕분에 깔끔한 JSON → XML → JSON 왕복이 가능합니다.

루트 요소가 여러 개인 XML을 변환할 수 있나요?

아닙니다. 최상위 요소가 여러 개이면 적격 XML이 아니므로 파서가 입력을 거부합니다. 먼저 조각들을 하나의 루트 요소로 감싸세요. <a/><b/><root><a/><b/></root>로 만든 다음 변환하면 됩니다.

결론

XML-JSON 변환은 단순 재포맷이 아니라 규칙에 따라 이뤄지며, 그 규칙은 어느 런타임에서나 일관됩니다. 속성은 @_ 키로, 혼합 콘텐츠 텍스트는 #text로, 반복 형제는 배열로 매핑되고, 값은 문자열로 유지되어 선행 0과 정밀도가 살아남습니다. 기억해 둘 함정은 단일-대-배열 형태 변화, 소리 없이 사라지는 속성, 주석과 네임스페이스 의미론의 손실입니다. 어느 것도 버그가 아니라, 그 뒤에 깔린 모델 불일치를 알고 나면 충분히 예측할 수 있는 동작입니다.

빠르고 비공개적인 변환이 필요할 때는 XML to JSON 변환기에 붙여넣으세요. 전적으로 브라우저 안에서 동작합니다. 먼저 XML 포맷터로 원본을 검증하고, 왕복 XML이 필요하면 JSON to XML 변환기로 반대 방향으로 변환하세요. 데이터 형식 모델이 변환 동작을 어떻게 빚어내는지 더 알고 싶다면, YAML과 JSON의 차이에 관한 노트를 참고하세요.