IT

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

lottoking 2020. 10. 10. 10:24

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


SAX 및 StAX와 같은 xml 파서는 DOM 파서와 같은 트리 구조를 구축하는 파서보다 빨리 메모리입니다. SAX는 푸시 파서로서 관찰자 패턴 (리스너 패턴이라고도 함)의 인스턴스임을 의미합니다. SAX가 처음에 있었지만 거기에 StAX가 나 있었다. 풀 파서로 기본적으로 반복자처럼 작동합니다.

어디에서나 SAX보다 StAX를 선호하는 이유를 사용할 수 있습니다.

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

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


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

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


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

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

즉, 방법이있을 것입니다

AddressType parseAddress(...); // A

또는

void parseAddress(...); // B

(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'시작 요소의 경우 실제 주소를 구문 분석하고 확인한 다음 주소를 해석하는 작업이있는 메서드로 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는 여러 이벤트를 제공 할 수 있습니다 (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 속도.


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

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


그것은 모두 균형입니다.

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

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

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

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


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

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


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

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

참고 URL : https://stackoverflow.com/questions/7521803/when-should-i-choose-sax-over-stax