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(...);
hoặc là
void parseAddress(...);
ở đâ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.
int event = reader.next();
if(event == XMLStreamConstants.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(..) {
XMLStreamReader reader = ....;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
break;
}
} while(reader.hasNext());
MyDataBindingObject object = new MyDataBindingObject();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever1")) {
WhateverObject child = parseSubTreeForWhatever(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
parseSubTreeForWhateverAtRelativeLevel2(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} 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();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("Whatever2")) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
if(level == 2) {
MyWhateverObject child = parseMySubelementTree(reader);
level --;
object.setWhatever(child);
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} 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();
int level = 1;
do {
int event = reader.next();
if(event == XMLStreamConstants.START_ELEMENT) {
level++;
if(reader.getLocalName().equals("FirstName")) {
String text = reader.getElementText()
if(text.length() > 0) {
myObject.setName(text)
}
level--;
} else if(reader.getLocalName().equals("LastName")) {
}
} else if(event == XMLStreamConstants.END_ELEMENT) {
level--;
}
} while(level > 0);
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 độ.