Khi nào tôi nên chọn SAX thay vì StAX?


81

Truyền trực tuyến các trình phân tích cú pháp xml như SAX và StAX nhanh hơn và tiết kiệm bộ nhớ hơn so với các trình phân tích cú pháp xây dựng cấu trúc cây như trình phân tích cú pháp DOM. SAX là một trình phân tích cú pháp đẩy, có nghĩa là nó là một thể hiện của mẫu người quan sát (còn gọi là mẫu người nghe). SAX đã có mặt đầu tiên, nhưng sau đó đến StAX - một trình phân tích cú pháp kéo, nghĩa là về cơ bản nó hoạt động giống như một trình lặp.

Bạn có thể tìm thấy lý do tại sao thích StAX hơn SAX ở mọi nơi, nhưng nó thường tóm gọn lại là: "nó dễ sử dụng hơn".

Trong hướng dẫn Java về JAXP StAX được trình bày một cách mơ hồ là trung gian giữa DOM và SAX: "nó dễ dàng hơn SAX và hiệu quả hơn DOM". Tuy nhiên, tôi chưa bao giờ tìm thấy bất kỳ manh mối nào cho thấy StAX sẽ chậm hơn hoặc kém hiệu quả hơn so với SAX.

Tất cả điều này khiến tôi tự hỏi: có lý do nào để chọn SAX thay vì StAX không?

Câu trả lời:


22

Để khái quát một chút, tôi nghĩ rằng StAXcó thể hiệu quả như SAX. Với thiết kế cải tiến của StAXtôi, tôi thực sự không thể tìm thấy bất kỳ tình huống nào mà SAXphân tích cú pháp sẽ được ưu tiên hơn, trừ khi làm việc với mã kế thừa.

CHỈNH SỬA : Theo blog này, Java SAX so với StAX StAX không cung cấp xác thực lược đồ.


2
không quá khó để thêm xác thực trên stax. đã tự thực hiện điều đó vào ngày hôm trước.
jtahlborn

Thông tin chi tiết về xác thực: stackoverflow.com/questions/5793087/stax-xml-validation
Ben,

81

Tổng quan
Tài liệu XML là tài liệu phân cấp, trong đó các tên phần tử và không gian tên giống nhau có thể xuất hiện ở một số nơi, có ý nghĩa khác nhau và ở độ sâu vô hạn (đệ quy). Như bình thường, giải pháp cho những vấn đề lớn là chia chúng thành những vấn đề nhỏ. Trong ngữ cảnh phân tích cú pháp XML, điều này có nghĩa là phân tích cú pháp các phần cụ thể của XML trong các phương thức dành riêng cho XML đó. Ví dụ, một đoạn logic sẽ phân tích cú pháp một địa chỉ:

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

tức là bạn sẽ có một phương pháp

AddressType parseAddress(...); // A

hoặc là

void parseAddress(...); // B

ở đâu đó trong logic của bạn, lấy các đối số đầu vào XML và trả về một đối tượng (kết quả của B có thể được tìm nạp từ một trường sau đó).

SAX
SAX 'đẩy' các sự kiện XML , để bạn xác định vị trí của các sự kiện XML trong chương trình / dữ liệu của bạn.

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

Trong trường hợp có phần tử bắt đầu 'Tòa nhà', bạn cần xác định rằng bạn đang thực sự phân tích cú pháp một Địa chỉ và sau đó định tuyến sự kiện XML tới phương thức có công việc diễn giải Địa chỉ.

StAX
StAX 'kéo' các sự kiện XML , để bạn xác định vị trí trong chương trình / dữ liệu của bạn để nhận các sự kiện XML.

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

Tất nhiên, bạn sẽ luôn muốn nhận được sự kiện 'Tòa nhà' theo phương pháp mà công việc của nó là diễn giải Địa chỉ.

Thảo luận
Sự khác biệt giữa SAX và StAX là push và pull. Trong cả hai trường hợp, trạng thái phân tích cú pháp phải được xử lý bằng cách nào đó.

Điều này chuyển thành phương pháp B là điển hình cho SAX và phương pháp A cho StAX. Ngoài ra, SAX phải cung cấp cho B các sự kiện XML riêng lẻ, trong khi StAX có thể cung cấp cho A nhiều sự kiện (bằng cách truyền một cá thể XMLStreamReader).

Vì vậy, B đầu tiên kiểm tra trạng thái trước đó của phân tích cú pháp và sau đó xử lý từng sự kiện XML riêng lẻ và sau đó lưu trữ trạng thái (trong một trường). Phương pháp A chỉ có thể xử lý tất cả các sự kiện XML cùng một lúc bằng cách truy cập XMLStreamReader nhiều lần cho đến khi hài lòng.

Kết luận
StAX cho phép bạn cấu trúc mã phân tích cú pháp (liên kết dữ liệu) của mình theo cấu trúc XML ; vì vậy trong mối quan hệ với SAX, 'trạng thái' là ẩn trong luồng chương trình cho StAX, trong khi trong SAX, bạn luôn cần phải bảo toàn một số loại biến trạng thái + định tuyến luồng theo trạng thái đó, đối với hầu hết các lệnh gọi sự kiện.

Tôi đề xuất StAX cho tất cả trừ các tài liệu đơn giản nhất. Thay vì chuyển sang SAX như một sự tối ưu hóa sau đó (nhưng có thể bạn sẽ muốn chuyển sang dạng nhị phân trước đó).

Làm theo mẫu này khi phân tích cú pháp bằng 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;
}

Vì vậy, phương pháp con sử dụng cùng một cách tiếp cận, tức là mức đếm:

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;
}

Và rồi cuối cùng bạn đạt đến một cấp độ mà bạn sẽ đọc các loại cơ sở.

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;
}

Điều này khá đơn giản và không có chỗ cho những hiểu lầm. Chỉ cần nhớ để giảm mức độ một cách chính xác:

A. sau khi bạn mong đợi các ký tự nhưng có END_ELEMENT trong một số thẻ phải chứa các ký tự (trong mẫu trên):

<Name>Thomas</Name>

thay vào đó là

<Name></Name>

Điều này cũng đúng với một cây con bị thiếu, bạn có thể hiểu được.

B. sau khi gọi các phương thức phân luồng con, được gọi trên các phần tử bắt đầu và trả về SAU phần tử kết thúc tương ứng, tức là trình phân tích cú pháp ở một mức thấp hơn trước khi gọi phương thức (mẫu trên).

Lưu ý rằng cách tiếp cận này cũng hoàn toàn bỏ qua khoảng trắng 'có thể bỏ qua' để triển khai mạnh mẽ hơn.

Phân tích cú pháp
Đi với Woodstox cho hầu hết các tính năng hoặc Aaalto-xml cho tốc độ.


Trong câu mở đầu của bạn, nó viết "... trong khi trong SAX ...". Đây có phải là lỗi đánh máy không? ("SAX" thay vì "StAX") Trong mọi trường hợp, cảm ơn vì câu trả lời. Nếu tôi hiểu bạn chính xác, bạn đang nói rằng trạng thái ngầm trong phương pháp SAX là một lợi ích so với nhu cầu theo dõi vị trí cây xml của bạn trong phương pháp StAX.
Rinke

Cảm ơn vì câu trả lời (bây giờ thậm chí còn phức tạp hơn). Tôi e rằng tôi vẫn không thấy lý do chính đáng để sử dụng SAX thay vì StAX. Câu trả lời của bạn là một lời giải thích tốt về cách hoạt động của cả hai bộ xử lý.
Rinke

Đối với các tài liệu đơn giản, chúng giống nhau. Hãy xem ví dụ lược đồ này: mpeg.chiariglione.org/technologies/mpeg-21/mp21-did/index.htm và StAX sẽ thực tế hơn.
ThomasRS

Tóm lại, vì bạn đang viết mã của mình, bạn hiểu phần nào của tài liệu mà bạn đang phân tích cú pháp, tức là tất cả logic để ánh xạ một sự kiện SAX là mã chính xác, đều bị lãng phí.
ThomasRS

16

@Rinke: Tôi đoán chỉ có lúc tôi nghĩ đến việc thích SAX hơn STAX trong trường hợp bạn không cần xử lý / xử lý nội dung XML; ví dụ: điều bạn muốn làm duy nhất là kiểm tra xem có đúng định dạng của XML đến không và chỉ muốn xử lý lỗi nếu nó có ... trong trường hợp này, bạn chỉ cần gọi phương thức parse () trên trình phân tích cú pháp SAX và chỉ định trình xử lý lỗi để xử lý bất kỳ vấn đề phân tích cú pháp .... vì vậy về cơ bản STAX chắc chắn là lựa chọn thích hợp hơn trong các tình huống mà bạn muốn xử lý nội dung do trình xử lý nội dung SAX quá khó để viết mã ...

một ví dụ thực tế của trường hợp này có thể là nếu bạn có một loạt các nút SOAP trong hệ thống doanh nghiệp của mình và một nút SOAP cấp đầu vào chỉ cho phép các SOAP XML đó chuyển qua giai đoạn tiếp theo đã được hình thành tốt, thì tôi không hiểu lý do gì khiến tôi sẽ sử dụng STAX. Tôi sẽ chỉ sử dụng SAX.


Tôi đã chọn câu trả lời này là câu trả lời tốt nhất cho đến nay. Mặc dù đó là một câu trả lời hay, tuy nhiên, tôi không cảm thấy nó có thẩm quyền và rõ ràng 100%. Câu trả lời mới được chào đón.
Rinke

1

Tất cả là một sự cân bằng.

Bạn có thể biến trình phân tích cú pháp SAX thành trình phân tích cú pháp kéo bằng cách sử dụng hàng đợi chặn và một số thủ thuật chuỗi, vì vậy, đối với tôi, có ít sự khác biệt hơn so với lúc đầu.

Tôi tin rằng hiện tại StAX cần được đóng gói thông qua một jar của bên thứ ba trong khi SAX miễn phí trong javax.

Gần đây tôi đã chọn SAX và xây dựng một trình phân tích cú pháp kéo xung quanh nó để tôi không cần phải dựa vào jar của bên thứ ba.

Các phiên bản Java trong tương lai gần như chắc chắn sẽ chứa một triển khai StAX để vấn đề sẽ biến mất.


1
Java SE 6 bao gồm StAX. Nhưng ví dụ: thực hiện android không bao gồm nó.
Bjarne Boström

0

StAX cho phép bạn tạo trình phân tích cú pháp XML hai chiều nhanh chóng. Nó chứng minh một giải pháp thay thế tốt hơn cho các phương pháp khác, chẳng hạn như DOM và SAX, cả về hiệu suất và khả năng sử dụng

Bạn có thể đọc thêm về StAX trong Hướng dẫn Java StAX


-1

Hầu hết thông tin được cung cấp bởi những câu trả lời đó đã hơi lỗi thời ... đã có một nghiên cứu toàn diện về tất cả các lib phân tích cú pháp XML trong bài báo nghiên cứu năm 2013 này ... hãy đọc nó và bạn sẽ dễ dàng thấy người chiến thắng rõ ràng (gợi ý: chỉ có một người chiến thắng thực sự) ...

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


1
Tôi đã đọc bài báo, người chiến thắng là StAX bằng cách sử dụng API con trỏ như trong XMLStreamReader.
Roland

rất hài hước :), ý bạn là người chiến thắng cuộc đua rùa :)
vtd-xml-author

Tôi chỉ đọc lại bài báo, và đúng là StaX vượt trội hơn vtd, nhanh hơn và tiêu thụ ít bộ nhớ hơn. Vậy quan điểm của bạn là gì?
Roland

người chiến thắng là stAX theo cách nào? bạn đang đề cập đến phần nào của bài báo? sửa đổi tài liệu, hoặc lựa chọn hoặc phân biệt? rõ ràng tác giả của bài báo đã rút ra một kết luận khác. nhưng họ có thể hoàn toàn sai ...
vtd-xml-author

1
Ví dụ trang 80: Theo kết quả (hình 11 và hình 12) chúng ta có thể thấy rằng StAX là API có hiệu suất tốt hơn, tiếp theo là VTD. Tuy nhiên, VTD tiêu tốn một lượng bộ nhớ đáng kể. Tiêu thụ bộ nhớ có thể là một nút thắt cổ chai đối với các môi trường cung cấp khả năng hạn chế.
Roland
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.