XML Parser

안준성·2024년 7월 17일
post-thumbnail

오늘은 XML 데이터를 파싱해 DOM Tree로 만드는 과정에 대해 알아볼 것이다.

먼저 XML이 무엇인지,
XML을 DOM Tree로 변환하는 이유,
DOM Tree로 변환하기 위한 과정에 대해서 알아보자.


XML

XML은 eXtensible Markup Language의 약자로,
번역하자면 확장 가능한 마크업 언어로
이름 그대로 확장성과 호환성 덕분에 널리 채택되었다.

XML은 데이터를 저장하고 전송하기 위해 설계된 마크업 언어로
사람과 기계 모두가 읽기 쉽도록
tag를 이용해 데이터를 구조화하여 표현한다.

XML은 텍스트 기반으로 어떤 플랫폼이나 언어에서도
동일한 방식으로 사용할 수 있다.
이는 시스템 간의 데이터 교환에 매우 유용하다.

또한 표준화된 스펙을 가지고 있어
다양한 도구와 라이브러리가 존재한다.

XML Schema와 Document Type Definition을 사용하여
구조와 내용을 검증할 수 있다.
이를 통해 데이터의 일관성을 보장한다.

다음은 XML 문서의 예시이다.

<bookstore>
  <book>
    <title lang="en">Harry Potter</title>
    <author>J.K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book>
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>

이건 예쁘게 출력해서 그렇고 실제로는 개행과 들여쓰기 없이 한 줄의 텍스트이다.

XML을 너무 깊게 알 필요는 없다고 생각하므로
이 정도만 정리하고 이제 이 XML을 DOM Tree로 변환하는 과정에 대해 알아보자.


DOM Tree

DOM Tree란 무엇인가.

Document Object Model Tree의 약자로,
XML같은 텍스트 문서를 프로그래밍 상에서 쉽게 다루기 위해
요소 단위로 분리하고 객체화 하여
Tree 자료구조에다 담아 놓은 것을 의미한다.
여기서 굳이 Tree에 담는 것은 문서가 계층 구조로 이뤄져 있기 때문이다.

이를 통해 문서의 각 요소를 객체로 접근 및 조작할 수 있으며,
문서의 구조와 내용을 쉽게 변경할 수 있다.

그렇다면 사실상 하나의 문자열로 들어오는 XML을
어떻게 분리분리 하여 트리에 담는 것일까.
그 과정에 대해 알아보자.

Tokenizer

우리는 하나의 문자열로 된 문서에서
요소 단위로 분리하여 객체화 하여야 한다.

이를 위해 먼저 의미 단위로 문자열을 분리해준다.

예를 들어 다음과 같은 XML이 있다고 했을 때

<note>
  <p>Text</p>
  <br/>
</note>

분리하고 난 모습은 다음과 같다.

[
  "<note>", 
  "<p>", 
  "Text", 
  "</p>", 
  "<br/>", 
  "</note>"
]

이렇게 분리한 문자열들을 토큰이라고 부르고,
분리하는 과정을 Tokenization이라고 한다.

Lexer

이제 이 토큰들의 배열을 돌면서
각 토큰이 어떤 타입인지 의미를 부여해주는 과정이 필요하다.
여기서 타입을 부여해주어야 트리에 담을 때 계층 구조에 맞게 담을 수 있다.

XML에서 의미를 부여하는 종류는 굉장히 많지만
시작태그, 종료태그, self-closing 태그, 텍스트
이 네 가지에 대해서만 분류해보자.

분류한 모습:

[
  { "type": "StartTag", "value": "note" }, 
  { "type": "StartTag", "value": "p" }, 
  { "type": "Text", "value": "Text" }, 
  { "type": "EndTag", "value": "p" }, 
  { "type": "SelfClosingTag", "value": "br" }, 
  { "type": "EndTag", "value": "note" }
]

이렇게 각 토큰들에 대한 어휘 분석 단계를 Lexing이라고 한다.

Parser

이제 어휘 분석이 끝난 토큰들을 트리에 담을 시간이다.
사실 이 부분이 제일 어렵다고 볼 수 있다.

이를 위해 스택과 활성 노드를 가리키는 포인터가 필요하다.
토큰들을 앞에서부터 순회하며
토큰의 타입에 따라 현재 토큰이 활성 노드의 자식인지 형제인지 결정된다.

구체적인 프로세스는 다음과 같다.

시작 태그(Start Element)인 경우
	현재 토큰을 노드로 생성
	생성한 노드를 활성 노드의 자식으로 추가
	활성 노드를 스택에 push
	새로운 노드를 활성 노드로 설정
텍스트(Text)인 경우
	활성 노드의 콘텐츠에 텍스트를 추가
종료 태그(End Element)인 경우
	활성 노드의 태그 값과 현재 토큰의 태그 값이 일치하는지 확인
		일치하면 스택에서 한 개를 pop하여 활성 노드로 설정
스스로 닫힌 태그(Self-Closing Element)인 경우
	현재 토큰을 노드로 생성
	이 노드를 활성 노드의 자식으로 추가

이러한 프로세스를 거치고 나면 토큰 배열이 Tree의 구조를 띄게 된다.


여기까지가 XML 문서를 DOM Tree로 변환하는 과정이다.
Tokenization -> Lexing -> Parsing으로 진행 되는 이 과정들은
일반적으로 Parsing이라고 총칭되며,
이러한 작업을 수행하는 모듈을 XML Parser라고 한다.

또한 이러한 과정들은 컴파일러의 컴파일 과정에서도 비슷하게 일어난다.

profile
안녕하세요

0개의 댓글