StAX보다 SAX를 언제 선택해야합니까?


81

SAX 및 StAX와 같은 스트리밍 xml 파서는 DOM 파서와 같은 트리 구조를 구축하는 파서보다 빠르고 메모리 효율적입니다. SAX는 푸시 파서로, 관찰자 ​​패턴 (리스너 패턴이라고도 함)의 인스턴스임을 의미합니다. SAX가 먼저 있었지만 StAX가 나왔습니다. 풀 파서 (pull parser)는 기본적으로 반복자처럼 작동합니다.

어디에서나 SAX보다 StAX를 선호하는 이유를 찾을 수 있지만 일반적으로 "사용하기 더 쉽습니다"로 요약됩니다.

JAXP에 대한 Java 튜토리얼에서 StAX는 DOM과 SAX 사이의 중간으로 모호하게 표시됩니다. "SAX보다 쉽고 DOM보다 효율적입니다". 그러나 StAX가 SAX보다 느리거나 메모리 효율성이 낮다는 단서를 찾지 못했습니다.

이 모든 것이 나를 궁금하게 만들었습니다. StAX 대신 SAX를 선택해야하는 이유가 있습니까?

답변:


22

조금을 일반화하기 위해, 나는 생각 StAX만큼 효율적이 될 수 있습니다 SAX. 향상된 디자인 으로 레거시 코드로 작업하지 않는 한 구문 분석이 선호되는 StAX상황을 실제로 찾을 수 없습니다 SAX.

편집 :이 블로그에 따르면 Java SAX 대 StAX StAX 는 스키마 유효성 검사를 제공하지 않습니다.


2
stax 위에 유효성 검사를 추가하는 것은 그리 어렵지 않습니다. 저 자신을 구현했습니다.
jtahlborn 2011 년

유효성 검사에 대한 자세한 내용 : stackoverflow.com/questions/5793087/stax-xml-validation
Ben

81

개요
XML 문서는 계층 적 문서로, 동일한 요소 이름과 네임 스페이스가 여러 위치에서 다른 의미를 가지며 무한 깊이 (재귀 적)로 나타날 수 있습니다. 일반적으로 큰 문제에 대한 해결책은 작은 문제로 나누는 것입니다. XML 구문 분석의 맥락에서 이는 해당 XML과 관련된 메서드에서 XML의 특정 부분을 구문 분석하는 것을 의미합니다. 예를 들어, 하나의 논리는 주소를 구문 분석합니다.

<Address>
    <Street>Odins vei</Street>    
    <Building>4</Building>
    <Door>b</Door>
</Address>

즉, 방법이있을 것입니다

AddressType parseAddress(...); // A

또는

void parseAddress(...); // B

논리의 어딘가에 XML 입력 인수를 가져와 객체를 반환합니다 (B의 결과는 나중에 필드에서 가져올 수 있음).

SAX
SAX는 XML 이벤트를 '푸시' 하므로 XML 이벤트가 프로그램 / 데이터에서 어디에 속하는지 결정하는 것은 사용자에게 달려 있습니다.

// method in stock SAX handler
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
    // .. your logic here for start element
}

'Building'시작 요소의 경우 실제로 Address를 구문 분석하고 있는지 확인한 다음 Address를 해석하는 작업이있는 메서드로 XML 이벤트를 라우팅해야합니다.

StAX
StAX는 XML 이벤트를 '풀링' 하므로 프로그램 / 데이터에서 XML 이벤트를 수신 할 위치를 결정하는 것은 사용자에게 달려 있습니다.

// method in standard StAX reader
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
    // .. your logic here for start element
}

물론 주소를 해석하는 작업이있는 메서드에서 항상 'Building'이벤트를 받고 싶을 것입니다.

토론
SAX와 StAX의 차이점은 밀고 당기는 것입니다. 두 경우 모두 구문 분석 상태를 어떻게 든 처리해야합니다.

이는 SAX의 경우 일반적인 방법 B와 StAX의 경우 방법 A로 변환됩니다. 또한 SAX는 B 개별 XML 이벤트를 제공해야하는 반면 StAX는 A에 여러 이벤트를 제공 할 수 있습니다 (XMLStreamReader 인스턴스 전달).

따라서 B는 먼저 구문 분석의 이전 상태를 확인한 다음 각 개별 XML 이벤트를 처리 한 다음 상태를 필드에 저장합니다. 메서드 A는 만족할 때까지 XMLStreamReader에 여러 번 액세스하여 XML 이벤트를 한 번에 모두 처리 할 수 ​​있습니다.

결론
StAX를 사용하면 XML 구조에 따라 구문 분석 (데이터 바인딩) 코드를 구조화 할 수 있습니다 . 따라서 SAX와 관련하여 '상태'는 StAX의 프로그램 흐름에서 암시 적이지만 SAX에서는 항상 일종의 상태 변수를 유지하고 대부분의 이벤트 호출에 대해 해당 상태에 따라 흐름을 라우팅해야합니다.

가장 간단한 문서를 제외한 모든 문서에 StAX를 권장합니다. 차라리 나중에 최적화로 SAX로 이동하십시오 (하지만 그때까지는 바이너리로 이동하고 싶을 것입니다).

StAX를 사용하여 구문 분석 할 때 다음 패턴을 따르십시오.

public MyDataBindingObject parse(..) { // provide input stream, reader, etc

        // set up parser
        // read the root tag to get to level 1
        XMLStreamReader reader = ....;

        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
              // check if correct root tag
              break;
            }

            // add check for document end if you want to

        } while(reader.hasNext());

        MyDataBindingObject object = new MyDataBindingObject();
        // read root attributes if any

        int level = 1; // we are at level 1, since we have read the document header

        do {
            int event = reader.next();
            if(event == XMLStreamConstants.START_ELEMENT) {
                level++;
                // do stateful stuff here

                // for child logic:
                if(reader.getLocalName().equals("Whatever1")) {
                    WhateverObject child = parseSubTreeForWhatever(reader);
                    level --; // read from level 1 to 0 in submethod.

                    // do something with the result of subtree
                    object.setWhatever(child);
                }

                // alternatively, faster
                if(level == 2) {
                    parseSubTreeForWhateverAtRelativeLevel2(reader);
                    level --; // read from level 1 to 0 in submethod.

                    // do something with the result of subtree
                    object.setWhatever(child);
                }


            } else if(event == XMLStreamConstants.END_ELEMENT) {
                level--;
                // do stateful stuff here, too
            }

        } while(level > 0);

        return object;
}

따라서 하위 방법은 거의 동일한 접근 방식, 즉 계산 수준을 사용합니다.

private MySubTreeObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {

    MySubTreeObject object = new MySubTreeObject();
    // read element attributes if any

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;
            // do stateful stuff here

            // for child logic:
            if(reader.getLocalName().equals("Whatever2")) {
                MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.

                // use subtree object somehow
                object.setWhatever(child);
            }

            // alternatively, faster, but less strict
            if(level == 2) {
              MyWhateverObject child = parseMySubelementTree(reader);
                level --; // read from level 1 to 0 in submethod.

                // use subtree object somehow
                object.setWhatever(child);
            }


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } while(level > 0);

    return object;
}

그리고 결국 기본 유형을 읽을 수준에 도달합니다.

private MySetterGetterObject parseSubTree(XMLStreamReader reader) throws XMLStreamException {

    MySetterGetterObject myObject = new MySetterGetterObject();
    // read element attributes if any

    int level = 1;
    do {
        int event = reader.next();
        if(event == XMLStreamConstants.START_ELEMENT) {
            level++;

            // assume <FirstName>Thomas</FirstName>:
            if(reader.getLocalName().equals("FirstName")) {
               // read tag contents
               String text = reader.getElementText()
               if(text.length() > 0) {
                    myObject.setName(text)
               }
               level--;

            } else if(reader.getLocalName().equals("LastName")) {
               // etc ..
            } 


        } else if(event == XMLStreamConstants.END_ELEMENT) {
            level--;
            // do stateful stuff here, too
        }

    } while(level > 0);

    // verify that all required fields in myObject are present

    return myObject;
}

이것은 매우 간단하며 오해의 여지가 없습니다. 레벨을 올바르게 낮추는 것을 잊지 마십시오.

A. 문자를 예상했지만 문자를 포함해야하는 일부 태그에 END_ELEMENT가있는 경우 (위 패턴에서) :

<Name>Thomas</Name>

대신이었다

<Name></Name>

누락 된 하위 트리도 마찬가지입니다. 아이디어를 얻을 수 있습니다.

B. 시작 요소에서 호출되고 해당 종료 요소 이후에 반환되는 하위 구문 분석 메서드를 호출 한 후, 즉 파서가 메서드 호출 이전보다 한 수준 아래에 있습니다 (위의 패턴).

이 접근 방식은보다 강력한 구현을 위해 '무시할 수있는'공백도 완전히 무시하는 방법에 유의하십시오.

파서
로 이동 Woodstox 대부분의 기능 또는 대한 Aaalto-XML 속도.


당신의 첫 문장에는 "... 반면에 SAX ..."라고 쓰여 있습니다. 오타입니까? ( "StAX"대신 "SAX") 어쨌든 답변 주셔서 감사합니다. 내가 올바르게 이해한다면 SAX 접근 방식의 암시 적 상태가 StAX 접근 방식에서 xml-tree 위치를 추적해야 할 필요성에 비해 이점이라고 말하는 것입니다.
Rinke 2011 년

(이제 더 정교한) 답변에 감사드립니다. 여전히 StAX 대신 SAX를 사용하는 좋은 이유가 무엇인지 모르겠습니다. 귀하의 대답은 두 프로세서가 어떻게 작동하는지에 대한 좋은 설명입니다.
Rinke 2011 년

간단한 문서의 경우 동일합니다. 예를 들어이 스키마를보십시오 : mpeg.chiariglione.org/technologies/mpeg-21/mp21-did/index.htm 및 StAX가 더 실용적입니다.
ThomasRS

간단히 말해서, 이미 코드를 작성하고 있기 때문에 구문 분석중인 문서의 어떤 부분, 즉 SAX 이벤트를 매핑하는 모든 논리가 올바른 코드인지를 이해하고 있습니다.
ThomasRS

16

@Rinke : XML 콘텐츠를 처리 / 처리 할 필요가없는 경우 STAX보다 SAX를 선호한다고 생각할 때만 생각합니다. 예를 들어 원하는 것은 들어오는 XML의 형식이 올바른지 확인하고 오류가있는 경우 오류를 처리하는 것입니다.이 경우 SAX 파서에서 parse () 메서드를 호출하고 오류 처리기를 지정하여 구문 분석 문제 .... 그래서 기본적으로 STAX는 SAX 콘텐츠 처리기가 코딩하기 너무 어렵 기 때문에 콘텐츠를 처리하려는 시나리오에서 확실히 선호되는 선택입니다.

이 사례의 한 가지 실용적인 예는 엔터프라이즈 시스템에 일련의 SOAP 노드가 있고 엔트리 레벨 SOAP 노드가 해당 SOAP XML이 잘 구성된 다음 단계를 통과하도록 허용하는 경우 일 수 있습니다. STAX를 사용합니다. SAX를 사용합니다.


이 답변을 지금까지 가장 좋은 답변으로 선택했습니다. 좋은 대답이지만 100 % 권위 있고 명확하다고 생각하지 않습니다. 새로운 답변을 환영합니다.
Rinke 2011

1

그것은 모두 균형입니다.

차단 대기열과 일부 스레드 속임수를 사용하여 SAX 파서를 풀 파서로 전환 할 수 있으므로 처음 보는 것보다 훨씬 적은 차이가 있습니다.

현재 StAX는 써드 파티 jar를 통해 패키징되어야하고 SAX는 javax에서 무료로 제공됩니다.

저는 최근에 SAX를 선택하고 그 주위에 풀 파서를 구축했기 때문에 타사 jar에 의존 할 필요가 없었습니다.

Java의 향후 버전은 거의 확실하게 StAX 구현을 포함하므로 문제가 해결됩니다.


1
Java SE 6에는 StAX가 포함되어 있습니다. 그러나 예를 들어 안드로이드 구현에는 그것을 포함하지 않습니다.
Bjarne Boström 2015

0

StAX를 사용하면 빠른 양방향 XML 파서를 만들 수 있습니다. 성능과 유용성 측면에서 DOM 및 SAX와 같은 다른 방법에 대한 더 나은 대안임을 입증합니다.

StAX에 대한 자세한 내용은 Java StAX 자습서 에서 읽을 수 있습니다.


-1

이러한 답변에서 제공하는 대부분의 정보는 다소 구식입니다 ...이 2013 년 연구 논문에서 모든 XML 구문 분석 라이브러리에 대한 포괄적 인 연구가있었습니다 ... 읽어 보면 확실한 승자를 쉽게 볼 수 있습니다 (힌트 : 진정한 승자) ...

http://recipp.ipp.pt/bitstream/10400.22/1847/1/ART_BrunoOliveira_2013.pdf


1
나는 논문을 읽었고 승자는에서와 같이 커서 API를 사용하는 StAX입니다 XMLStreamReader.
Roland

아주 재미 :), 당신은 : 거북이 경주의 우승자 의미
VTD-XML 저자

나는 단지 논문을 다시 읽었고 네 StaX는 vtd보다 우수하고 더 빠르고 적은 메모리 소비입니다. 그래서 당신의 요점은 무엇입니까?
롤랜드

승자는 어떤면에서 stAX입니까? 논문의 어느 부분을 언급하고 있습니까? 문서 수정, 선택 또는 차별화? 분명히 논문의 저자는 다른 결론을 내 렸습니다. 하지만 그들은 ... 완전히 잘못 될 수
VTD-XML 저자

1
예 : 80 페이지 : 결과 (그림 11 및 그림 12)에 따르면 StAX가 성능이 더 좋은 API이고 VTD가 뒤따른다는 것을 알 수 있습니다. 그러나 VTD는 상당한 양의 메모리를 사용합니다. 메모리 소비는 제한된 기능을 제공하는 환경에서 병목 현상이 될 수 있습니다.
롤랜드
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.