XML 파싱 기법


11

나는 XML을 처리하기가 다소 번거 롭다는 것을 항상 발견했다. 나는 XML 파서를 구현하는 것에 대해 이야기하는 것이 아니라 노드별로 XML을 처리하는 SAX 파서와 같은 기존 스트림 기반 파서를 사용 하는 것에 대해 이야기하고 있습니다.

예, 이러한 파서에 대한 다양한 API를 배우는 것은 정말 쉽지만 XML을 처리하는 코드를 볼 때마다 항상 다소 복잡하다는 것을 알게됩니다. 근본적인 문제는 XML 문서가 논리적으로 개별 노드로 분리되어 있지만 데이터 유형 과 속성이 종종 여러 레벨의 중첩에 의해 실제 데이터와 분리되는 것 같습니다. 따라서 특정 노드를 개별적으로 처리 할 때는 현재 위치다음에 수행 할 작업 을 결정하기 위해 많은 추가 상태를 유지 해야합니다.

예를 들어, 일반적인 XML 문서의 스 니펫이 제공된 경우 :

<book>
  <title>Blah blah</title>
  <author>Blah blah</author>
  <price>15 USD</price>
</book>

... 책 제목이 포함 된 텍스트 노드를 발견 한 시점을 어떻게 알 수 있습니까? 우리가 호출 할 때마다 XML 문서의 다음 노드를 제공하는 반복자처럼 작동하는 간단한 XML 파서가 있다고 가정하십시오 XMLParser.getNextNode(). 필자는 다음과 같은 코드를 작성해야합니다.

boolean insideBookNode = false;
boolean insideTitleNode = false;

while (!XMLParser.finished())
{
    ....
    XMLNode n = XMLParser.getNextNode();

    if (n.type() == XMLTextNode)
    {
        if (insideBookNode && insideTitleNode)
        {
            // We have a book title, so do something with it
        }
    }
    else
    {
        if (n.type() == XMLStartTag)
        {
            if (n.name().equals("book")) insideBookNode = true
            else if (n.name().equals("title")) insideTitleNode = true;
        }
        else if (n.type() == XMLEndTag)
        {
            if (n.name().equals("book")) insideBookNode = false;
            else if (n.name().equals("title")) insideTitleNode = false;
        }
    }
}

기본적으로 XML 처리는 이전에 찾은 부모 노드를 나타내는 데 사용되는 많은 상태 변수와 함께 거대한 상태 머신 기반 루프로 빠르게 전환됩니다. 그렇지 않으면 중첩 된 모든 태그를 추적하기 위해 스택 객체를 유지 관리해야합니다. 이로 인해 오류가 발생하기 쉽고 유지 관리가 어려워집니다.

다시 말하지만, 우리가 관심있는 데이터가 개별 노드와 직접적으로 연관되어 있지 않은 것 같습니다. 물론 다음과 같이 XML을 작성하면 가능할 수 있습니다.

<book title="Blah blah" author="blah blah" price="15 USD" />

... 실제로 XML이 사용되는 방식은 거의 없습니다. 대부분 우리는 부모 노드의 자식으로 텍스트 노드를 가지고 있으며 텍스트 노드가 무엇을 의미하는지 결정하기 위해 부모 노드를 추적해야합니다.

그래서 ... 내가 뭔가 잘못하고 있습니까? 더 좋은 방법이 있습니까? XML 스트림 기반 구문 분석기를 사용하는 것이 어느 시점에서 너무 번거로워서 본격적인 DOM 구문 분석기가 필요합니까? 스트림 기반 파서를 사용하여 XML을 처리 할 때 어떤 종류의 관용구가 사용되는지 다른 프로그래머들로부터 듣고 싶습니다. 스트림 기반 XML 구문 분석이 항상 거대한 상태 머신으로 전환되어야합니까?


2
.net 언어를 사용하는 경우 linq to xml aka XLinq를 살펴보십시오.
Muad'Dib

고맙습니다, 나는이 문제가있는 유일한 사람이라고 생각했습니다. 솔직히, 나는 종종 전체 XML 형식이 도움보다 방해가된다는 것을 알게되었다. 예, 많은 텍스트를 작은 텍스트 파일에 저장할 수 있습니다. 그러나 20 개 이상의 클래스가 필요하다면 포장을 풀고 의미를 이해하십시오. 그것은 Monty Python의 Holy Grail의 토끼와 같습니다.
Elise van Looij 2016 년

답변:


9

나에게, 문제는 다른 길입니다. XML 문서는 어느 시점에서 귀찮게되므로 DOM 대신 SAX를 사용해야합니까?

나는 매우 크고 결정 불가능한 크기의 데이터 스트림에 대해서만 SAX를 사용합니다. 또는 XML을 호출하려는 동작이 실제로 이벤트 중심이므로 SAX와 유사합니다.

당신이주는 예제는 나에게 DOM처럼 보입니다.

  1. XML로드
  2. 제목 노드를 추출하고 "함께 무언가를하십시오".

편집 : 나는 형식이 잘못된 스트림에 대해 SAX를 사용하지만 데이터를 가져올 때 가장 잘 추측 할 수있는 곳입니다.


2
나는 이것이 좋은 지적이라고 생각합니다. DOM에 비해 너무 큰 문서를 구문 분석하는 경우 XML에
Dean Harding

1
+1 : 옵션이 주어지면 항상 DOM을 사용합니다. 불행히도, 우리의 설계 요구 사항에는 항상 "모든 크기의 문서를 처리 할 수있는 기능"과 "성능을 가져야합니다"가 포함되어 있으며 이는 DOM 기반 솔루션을 거의 배제합니다.
TMN

3
@TMN은 이상적인 세계에서 요구 사항이 처음부터 XML을 배제한다는 것입니다.
SK-logic

1
@TMN은 이러한 팬텀 요구 사항 중 하나 인 것 같습니다. "물론 우리의 모든 문서는 약 100KB에 불과하며, 우리가 본 것 중 가장 큰 것은 1MB입니다. 그러나 미래가 무엇인지 알지 못하므로 옵션을 계속 열어 두어야합니다. 무한히 큰 문서를 만들 수 있습니다 "
Paul Butcher

@Paul Butcher, 당신은 모른다. Wikipedia 덤프는 30GB의 XML과 같습니다.
Channel72

7

XML을 너무 많이 사용하지는 않습니다. 제 생각에는 XML을 라이브러리로 구문 분석하는 가장 좋은 방법 중 하나는 XPath를 사용하는 것입니다.

특정 노드를 찾기 위해 트리를 탐색하는 대신 경로를 제공합니다. 예 (의사 코드)의 경우 다음과 같습니다.

books = parent.xpath ( "/ book") // 모든 책 노드를 제공합니다
책의 각 책
    제목 = book.xpath ( "/ title / text ()")
    저자 = book.xpath ( "/ author / text ()")
    가격 = book.xpath ( "/ price / text ()")

    // 데이터로 작업

XPath는 그보다 훨씬 강력합니다. 조건과 값을 사용하여 검색하고 목록에서 특정 노드를 선택하고 트리를 통해 레벨을 이동할 수 있습니다. 사용 방법에 대한 정보를 찾는 것이 좋습니다. 많은 구문 분석 라이브러리에서 구현됩니다 (파이썬에는 .Net Framework 버전과 lxml을 사용합니다)


XML이 구성되는 방식을 미리 알고 신뢰할 수 있다면 괜찮습니다. 예를 들어, 요소의 너비가 노드의 속성으로 지정되는지 또는 요소의 크기 노드 내부에 속성 노드로 지정되는지 여부를 모른다면 XPath가 큰 도움이되지 않습니다.
Elise van Looij 2016 년

5

스트림 기반 XML 구문 분석이 항상 거대한 상태 머신으로 전환되어야합니까?

보통 그렇습니다.

필자가 본격적인 DOM 파서를 사용하도록 지시하는 것은 문서 내에서 상호 참조를 해결할 수 있도록 메모리 내 파일 계층 구조의 일부를 모방해야 할 때입니다.


+1 : DOM으로 시작하십시오. SAX를 피하십시오.
S.Lott

또는 vtd-xml과 함께
vtd-xml-author

4

일반적으로 파싱은 단순히 상태 머신을 구동하는 것이며 XML 파싱은 다르지 않습니다. 스트림 기반 파싱은 항상 번거 롭다. 나는 항상 조상 노드를 추적하기 위해 일종의 스택을 구축하고 태그 또는 경로 레지스트리를 확인하고 이벤트를 발생시키는 많은 이벤트와 이벤트 디스패처를 정의한다. 하나가 일치하면. 핵심 코드는 상당히 타이트하지만 다음 텍스트 노드의 값을 구조의 필드에 어딘가에 할당하는 것으로 구성된 거대한 이벤트 핸들러로 마무리됩니다 . 비즈니스 로직을 혼합 해야하는 경우 꽤 털이 될 수 있습니다.

크기 나 성능 문제가 달리 명시하지 않는 한 항상 DOM을 사용합니다.


1

완전히 언어에 구애받지는 않지만, 일반적으로 구문 분석을 생각하기보다는 XML을 객체로 직렬화 해제합니다. 구문 분석 전략 자체에 대해 걱정할 시간은 속도 문제가있는 경우입니다.


파싱에 해당합니다. 문제의 XML이 객체 직렬화의 출력이 아니라면 이미 구축 된 직렬화 해제 라이브러리가 있습니다. 그러나이 질문은 나타나지 않습니다.

많은 언어 / 스택에는 직렬화 해제 라이브러리가 준비되어 있습니다.
Wyatt Barnett

그래, 뭐? 야생에서 모든 XML 파일과 같은 형식으로 와서 당신이하는 하나이 경우 그냥 직렬화 라이브러리를 사용하지 구문 분석처럼, 당신은이 질문을하지 않습니다 - 내 포인트는 가만히 아무것도 , 당신의 자신에를 시내에서 또는 다른 방법으로.

0

XPath를 사용할 수 있다면 훨씬 번거로워집니다. 그리고 .Net에서 LINQ to XML은 매력적이지 않은 많은 것들을 추상화합니다. ( 편집 -물론 DOM 접근 방식이 필요합니다)

기본적으로 스트림 기반 접근 방식을 사용하는 경우 (DOM이 필요한 더 좋은 추상화를 사용할 수 없음) 항상 번거로울 것이라고 생각하며 이에 대한 방법이 확실하지 않습니다.


XPath를 사용하는 경우 자체 개발 한 XPath 평가 기와 함께 사용하지 않는 한 DOM을 사용합니다.
TMN

그렇습니다. 따라서 DOM을 필요로하는 추상화에 대한 제 의견은 ... 감사합니다.
Steve

0

이터레이터를 제공하는 파서를 찾을 수 있다면,이를 어휘 분석기로 취급하고 상태 머신 생성기를 사용하는 것을 생각 했습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.