XML JSON 변환 시 배열·속성이 바뀌는 이유

JSON 배열을 XML로 바꾸면 배열을 감싸는 태그가 새로 생기지 않고, 같은 이름의 태그가 항목 수만큼 그냥 나란히 반복되는 구조로 바뀝니다. 반대로 XML의 속성(attribute)은 JSON으로 바꾸면 다른 값들과 섞이지 않고 @attributes 같은 별도 키 아래로 들어갑니다. 둘 다 버그가 아니라 XML과 JSON이 애초에 담을 수 있는 정보의 형태 자체가 다르기 때문에 생기는 결과입니다.
요약 ① JSON 배열은 XML에 배열이라는 개념이 없어서, 감싸는 태그 없이 같은 이름의 태그가 항목 수만큼 반복되는 구조로 바뀝니다. ② XML 요소는 속성·자식 요소·텍스트 세 가지를 동시에 가질 수 있지만 JSON 객체는 이 구분이 없어서, 변환기는 속성을 @attributes 같은 별도 키로 분리해 담습니다. ③ 이 표기 방식은 XML·JSON 표준이 정한 규칙이 아니라 변환기가 정하는 관례라서, 도구가 다르면 결과 JSON의 모양도 조금씩 달라질 수 있습니다.
문제
XML로 오는 데이터를 JSON으로 바꿔서 코드에서 쓰다 보면 두 지점에서 막힙니다. 하나는 "같은 태그가 여러 개면 배열인데, 왜 한 개일 때는 배열이 아니라 객체 하나로만 나오는가"이고, 다른 하나는 "category="cooking" 같은 속성값이 title·author 같은 다른 필드와 나란히 있지 않고 왜 @attributes라는 낯선 키 안에 따로 들어가는가"입니다.
반대 방향도 마찬가지입니다. JSON 배열 데이터를 XML 규격으로 내보내야 하는 상황에서, 배열을 감싸는 태그가 자동으로 생기지 않고 항목 개수만큼 같은 이름의 태그만 반복되는 결과를 처음 보면 "배열 구조가 사라진 것 아닌가" 하고 당황하게 됩니다.
이 둘 모두 변환기가 잘못 만든 결과가 아니라, XML과 JSON이라는 두 포맷이 표현할 수 있는 정보의 형태가 다르기 때문에 생기는 필연적인 결과입니다.
해결
두 방향을 규칙으로 정리하면 도구 없이도 결과 모양을 미리 예상할 수 있습니다.
JSON 배열 → XML: 왜 태그가 반복되는가
XML에는 배열이라는 자료형이 없습니다. 있는 것은 "요소(element)"뿐이고, 요소는 이름이 같은 채로 여러 번 나란히 나타날 수 있습니다. 그래서 JSON의 "book": [ {...}, {...} ]를 XML로 옮길 때는 배열을 감싸는 새 태그를 만드는 대신, 배열 안 항목 수만큼 <book> 태그를 반복해서 나열하는 방식을 씁니다. 항목이 2개면 <book>이 2번, 5개면 5번 나옵니다.
XML 속성 → JSON: 왜 @attributes로 분리되는가
XML 요소 하나는 속성(예: category="cooking"), 자식 요소(예: <title>), 텍스트(태그 사이의 글자) 세 가지를 동시에 가질 수 있습니다. 반면 JSON 객체는 키-값 쌍만 있을 뿐, "이 값이 속성인지 자식 요소인지"를 구분하는 문법이 없습니다. 이 구분이 사라지지 않도록 변환기는 속성을 @attributes라는 별도 키 아래로 몰아넣어 자식 요소·텍스트와 섞이지 않게 합니다. 요소에 속성과 텍스트가 동시에 있으면 텍스트는 다시 #text라는 키로 따로 담깁니다.
속성도 자식 요소도 없으면 문자열 그대로
반대로 요소에 속성도 자식 요소도 없이 텍스트만 있으면(예: <author>Giada De Laurentiis</author>) 굳이 객체로 감싸지 않고 그 값 자체를 문자열로 바로 돌려줍니다. {"author": "Giada De Laurentiis"}처럼 단순하게 나오는 이유입니다.
JSON 최상위 키가 여러 개면 감싸는 태그가 자동으로 생긴다
XML은 최상위 요소가 정확히 하나여야 합니다. 그래서 변환할 JSON 최상위에 키가 하나뿐이면 그 키 이름을 그대로 루트 태그로 쓰지만, 키가 두 개 이상이면 감쌀 태그가 없으므로 변환기가 <root>라는 태그를 임의로 만들어 그 안에 모든 키를 자식으로 넣습니다.
이 네 가지 규칙만 알아도 XML과 JSON을 오갈 때 결과 구조가 왜 이렇게 나오는지 대부분 설명이 됩니다. 실제 예시로 눈으로 직접 저희 도구로 확인해보세요.
XML을 붙여넣으면 JSON으로, JSON을 붙여넣으면 XML로 즉시 양방향 변환해서 결과를 확인할 수 있습니다.

자주 막히는 지점
| 상황 | 이유 |
|---|---|
| 같은 이름의 자식이 1개면 배열이 아니라 객체로 나옴 | 두 번째로 같은 이름이 나타날 때부터 배열로 바뀌는 규칙이라, 항목 수가 1개일 때와 2개 이상일 때 이후 코드가 다르게 동작할 수 있습니다 |
| 숫자로 보이는 값이 따옴표 붙은 문자열로 나옴 | XML 텍스트에는 타입 개념이 없어서 2005나 30.00 같은 값도 전부 문자열로 변환됩니다 |
| 속성·자식 요소·텍스트가 한 태그에 섞여 있음 | 속성은 @attributes, 자식 요소는 해당 태그 이름, 남는 텍스트는 #text 키로 각각 나뉘어 담깁니다 |
| JSON 최상위에 키가 여러 개 | 감쌀 태그가 없으므로 변환기가 자동으로 root라는 태그를 만들어 전체를 감쌉니다 |
| JSON 값이 null | 내용 없는 self-closing 태그(<tag/>)로 바뀝니다 |
정리
- JSON 배열은 XML에 배열 개념이 없어서, 감싸는 태그 없이 같은 이름의 태그가 항목 수만큼 반복되는 구조로 바뀝니다.
- XML 요소는 속성·자식 요소·텍스트를 동시에 가질 수 있지만 JSON 객체는 이 구분이 없어서, 변환기는 속성을 @attributes, 남는 텍스트를 #text라는 별도 키로 분리합니다.
- 속성도 자식 요소도 없이 텍스트만 있는 요소는 객체로 감싸지 않고 문자열 값 그대로 나옵니다.
- XML은 최상위 요소가 하나여야 하므로, JSON 최상위 키가 여러 개면 변환기가 자동으로 root 태그를 만들어 감쌉니다.
- 이 표기 방식은 XML·JSON 표준이 정한 규칙이 아니라 변환기가 정하는 관례라서, 다른 도구로 변환하면 키 이름이 다르게 나올 수도 있습니다.
XML을 붙여넣으면 JSON으로, JSON을 붙여넣으면 XML로 즉시 양방향 변환해서 결과를 확인할 수 있습니다.
자주 묻는 질문
자식 요소가 1개뿐이어도 배열로 나오게 할 수 없나요?
지금 이 변환 규칙은 같은 이름의 요소가 두 번째로 나타날 때부터 배열로 바꾸는 방식이라, 요소가 1개뿐이면 객체 하나로만 나옵니다. 항목 수와 무관하게 항상 배열로 다루고 싶다면, 변환된 JSON을 받은 뒤 코드에서 값이 배열이 아닐 경우 배열 하나로 감싸주는 처리를 직접 추가해야 합니다.
@attributes, #text라는 이름은 정해진 표준인가요?
XML과 JSON 사이에는 이런 이름을 강제하는 공식 표준이 없습니다. @attributes와 #text는 이 변환기가 속성·텍스트를 다른 값과 구분하기 위해 채택한 표기법이며, 다른 라이브러리나 도구는 속성 이름 앞에 직접 @를 붙이는 등 다른 방식을 쓰기도 합니다. 그래서 변환한 JSON을 다른 시스템에 넘길 때는 상대측이 같은 표기법을 쓰는지 먼저 확인하는 게 안전합니다.
JSON을 XML로 바꿀 때 루트 태그 이름을 원하는 대로 정할 수 있나요?
JSON 최상위에 키가 정확히 하나뿐이면 그 키 이름이 그대로 루트 태그가 되므로, 원하는 루트 이름을 그 키로 미리 정해두면 됩니다. 최상위 키가 여러 개면 감쌀 태그가 없어 자동으로 root라는 이름이 붙으므로, 원하는 이름을 쓰려면 변환 전 JSON을 { "원하는이름": { ...나머지 키 } } 형태로 한 번 감싸주면 됩니다.
