상세 컨텐츠

본문 제목

[Javascript/React] Parse XML to JSON / XML을 JSON으로 만들기

코딩테스트/알고리즘 분석

by bydawn25 2021. 12. 3. 18:04

본문

지금하고 있는 프로젝트에서 XML을 읽어서 해석해야하는 부분이 존재했다.

개발자 인생 XML은 정말 마주하고 싶지 않은 데이터 형식인데 애석하게도 예전 표준을 따르는 많은 디바이스들이 XML을 사용하고 있다고 한다.😢

 

 

 

 

 

일단 나는 발등에 급한 불이 떨어졌으므로 XML을 검색한다 .. 음 .. 모르겠다

 

그리고 How to convert JSON from XML을 검색한다. 다양한 라이브러리와 Funciton들이 제공된다.

- XML Document

- X2JS

- DomParse(가장 기초적인 xml접근 라이브러리인듯?)

- react-xml-parser

 

언어의 갯수만큼 해당하는 기능을 제공하는 라이브러리가 있는듯 .. !! 하지만 나는 현재 Front로 리액트를 사용하므로 react-xml-parser같은 라이브러리를 사용해서 한방에 빡 해결하고 싶었지만 내부사정상 해당 라이브러리를 사용하는것은 옵션에서 제외해야 했다 ㅠㅠ.

 

만약 가능하다면 당연히 라이브러리를 쓰도록 하자. 라이브러리가 있는데 굳이 같은 기능을 구현하는짓은 연습으로만 하자👍

 

 

 

 

 

많은 사람들이 사용할 수록 고려해야하는건 호완성! 그래서 MDN에서 제공하는 가장 표준적인 function인 DomParser()를 사용하기로 결정. 정해진 패턴이 있는 XML이라 약간의 수정과 추가를 통해 퍼스널한 Parsing알고리즘을 구현하였다.

 

참고한 소스는 아래 두개이다.

https://stackoverflow.com/questions/50442948/how-to-parse-xml-file-in-react

 

How to parse xml file in react?

I have tried using some libraries but I can not seem to find any answer. i have a React site and I am uploading a file using a form. I'm looking for a way to parse the XML file, and reach it's chil...

stackoverflow.com

https://stackoverflow.com/questions/1773550/convert-xml-to-json-and-back-using-javascript

 

Convert XML to JSON (and back) using Javascript

How would you convert from XML to JSON and then back to XML? The following tools work quite well, but aren't completely consistent: xml2json Has anyone encountered this situation before?

stackoverflow.com

 

 

 

 

아래는 전체코드이다


class Main extends Component {
     constructor(props) {
         super(props);
     }

     xml2json(xmlChildren,tempAry) {
         const lastCount = xmlChildren.childElementCount;
         const childLen = xmlChildren.attributes.length;
         const parentName = xmlChildren.nodeName;

         var obj = {};
         var lastObj = {};

         if(lastCount == 0) { //data 불러오기, 마지막 return으로 종료하는 부분
            lastObj[parentName] = {};
             for(let j=0;j<childLen;j++) lastObj[parentName][xmlChildren.attributes[j].nodeName] = xmlChildren.attributes[j].nodeValue;
             return lastObj;
         }

         for(let i=0; i<lastCount; i++){
             if(childLen>0) {
                 obj[parentName] = {};
                 for(let j=0;j<childLen;j++) obj[parentName][xmlChildren.attributes[j].nodeName] = xmlChildren.attributes[j].nodeValue;
             }
             
             const subPart = this.xml2json(xmlChildren.children[i],tempAry);

             if(subPart !== undefined && Object.keys(obj).length !== 0) {
                 obj[parentName][Object.keys(dataPart)[0]] = Object.values(dataPart)[0];
                 tempAry.push(obj);
             }
         }
     }

     xmlParsing() {
         const raw = 
         `<?xml version="1.0" encoding="EUC-KR" standalone="yes"?>
         <Invoice>
             <First coordination="350,1" width="120" height="15" lineThickness="0.0" bgColor="230,230,230" dash="false">
                 <Data value="고현정언니" fontName="Dialog.bold" fontSize="15" fontBold="true" fontItalic="false" align="center"/>
             </First>
             <Second coordination="700,1" width="120" height="15" lineThickness="0.0" bgColor="230,230,230" dash="false">
                 <Data value="최고에요" fontName="Dialog.bold" fontSize="18" fontBold="true" fontItalic="false" align="right"/>
             </Second>
         </Invoice>`;

         const parser = new DOMParser();
         const xml = parser.parseFromString(raw, 'text/xml');

         const tempAry = [];
         const tempDic = {};

         this.xml2json(xml.children[0],tempAry,tempDic);
         tempDic['Invoice'] = tempAry;
     }
 }

먼저 xml이 source로 들어왔다고 가정하자. 너를 닮은 사람 드라마 진짜 너무 재밌다 ㅋㅋㅋ 요즘에 정신놓고 보는중 ... 고현정 배우님을 사랑하기 때문에 Data라는 태그에 고현정언니를 넣어보았다. 별 시덥지 않은 이야기지만 ㅋㅋㅋ

 

 

 

 

일단 xml을 html요소로 변환해주자. 이때 DomParser() constructor가 사용된다. 쉽죠잉~?


const parser = new DOMParser();
const xml = parser.parseFromString(raw, 'text/xml');

 

 

 

Json Dictonary를 만들어주기 위해서 tempDic을, 결과를 모두 array에 담기 위해서 tempAry변수를 만들었다.


const tempAry = [];
const tempDic = {};

 

 

 

 

이제 parsing하는 부분인 xml2json부분을 보자. 처음에 아래 command로 호출을 하게 되는데. 디자인상 Invoice라는 태그가 항상 삽입이 되므로 Invoice 아래 요소들, 즉 children부터 읽으면 된다는 의미로 childeren[0]을 인자로 넘겨줬다.


this.xml2json(xml.children[0],tempAry,tempDic);

xml2Json는 보시다시피 재귀함수다. xml은 태그가 중첩되어 써 내려가기 때문에 요소 밑에 요소, 또 요소 밑에 요소 이런 식으로 나오게 되는데 어떻게 하면 계속해서 반복하며 효율적으로 같은 function을 호출할 수 있을까 고민하다가 재귀함수로 구현하기로 했다.

 

재귀함수로 구현했으면 나와야 하는 부분이 종료구문! 아래 종료구문의 조건은 단순히 더 이상 child를 가지지 않으면 parsing할 필요가 없으므로 계속해서 xml2json함수를 호출하는것을 멈추도록 했다. 마지막 요소의 attribute들을 dictionary형태로 가져와서 부모 json에 저장한다. 나는 항상 마지막 요소가 부모 요소의 일부로 들어가야 하는 상황이라 따로 형성해서 리턴하도록 했고 이것을 종료조건으로 사용했다.


if(lastCount == 0) { //data 불러오기
  lastObj[parentName] = {};
  for(let j=0;j<childLen;j++) lastObj[parentName][xmlChildren.attributes[j].nodeName] = xmlChildren.attributes[j].nodeValue;
  return lastObj;
}

부모 부분도 동일하다.

1. 자식이 있는 태그라면 자신의 정보를 json형태로 저장하고

2. 자식이 다시한번 자신을 점검할 수 있도록 xml2json함수를 호출한다.

3. 그리고 마지막으로 모든 자식들이 있어서 json이 형성된 상태라면 이를 결과 ary에 삽입한다.


for(let i=0; i<lastCount; i++){
  if(childLen>0) {
  	obj[parentName] = {};
  	for(let j=0;j<childLen;j++) obj[parentName][xmlChildren.attributes[j].nodeName] = xmlChildren.attributes[j].nodeValue;
  }

  const subPart = this.xml2json(xmlChildren.children[i],tempAry);

  if(subPart !== undefined && Object.keys(obj).length !== 0) {
  	obj[parentName][Object.keys(dataPart)[0]] = Object.values(dataPart)[0];
  	tempAry.push(obj);
  }
}

 

 

 

 

위와 같은 과정을 거치면 아래와 같은 결과를 확인할 수 있다.

 

 

 

 

 

너무 마음에 드는 결과다. 이제 이 결과를 이용해서 예쁜 그림들을 그려보는 일이 남았다.

성공하면 포스팅을 남길테니 다들 기대해죠라 ... 기도도 해죠라 ....

관련글 더보기