Tôi sẽ cung cấp câu trả lời theo định hướng Q & A chung cho câu hỏi này:
Trả lời câu hỏi
Tại sao chúng ta cần trình phân tích cú pháp XML?
Chúng tôi cần trình phân tích cú pháp XML vì chúng tôi không muốn làm mọi thứ trong ứng dụng của mình từ đầu và chúng tôi cần một số chương trình hoặc thư viện "trợ giúp" để làm một cái gì đó ở mức độ thấp nhưng rất cần thiết đối với chúng tôi. Những điều cấp thấp nhưng cần thiết này bao gồm kiểm tra tính chính xác, xác thực tài liệu dựa trên DTD hoặc lược đồ của nó (chỉ để xác thực trình phân tích cú pháp), giải quyết tham chiếu ký tự, hiểu các phần CDATA, v.v. Các trình phân tích cú pháp XML chỉ là các chương trình "trợ giúp" như vậy và chúng sẽ thực hiện tất cả các công việc này. Với trình phân tích cú pháp XML, chúng tôi được bảo vệ khỏi rất nhiều sự phức tạp này và chúng tôi có thể tập trung vào việc lập trình ở cấp độ cao thông qua API do các trình phân tích cú pháp triển khai và do đó đạt được hiệu quả lập trình.
Cái nào tốt hơn, SAX hay DOM?
Cả trình phân tích cú pháp SAX và DOM đều có những ưu điểm và nhược điểm. Cái nào tốt hơn nên phụ thuộc vào đặc điểm của ứng dụng của bạn (vui lòng tham khảo một số câu hỏi dưới đây).
Trình phân tích cú pháp nào có thể nhận được tốc độ tốt hơn, trình phân tích cú pháp DOM hoặc SAX?
Trình phân tích cú pháp SAX có thể có tốc độ tốt hơn.
Sự khác biệt giữa API dựa trên cây và API dựa trên sự kiện là gì?
API dựa trên cây được tập trung xung quanh cấu trúc cây và do đó cung cấp giao diện trên các thành phần của cây (là tài liệu DOM) như giao diện Tài liệu, giao diện Node, giao diện NodeList, giao diện Element, giao diện Attr, v.v. Tuy nhiên, ngược lại, API dựa trên sự kiện cung cấp giao diện trên trình xử lý. Có bốn giao diện xử lý, giao diện ContentHandler, giao diện DTDHandler, giao diện EntityResolver và giao diện ErrorHandler.
Sự khác biệt giữa Trình phân tích cú pháp DOM và Trình phân tích cú pháp SAX là gì?
Trình phân tích cú pháp DOM và trình phân tích cú pháp SAX hoạt động theo các cách khác nhau:
Trình phân tích cú pháp DOM tạo cấu trúc cây trong bộ nhớ từ tài liệu đầu vào và sau đó chờ yêu cầu từ máy khách. Nhưng một trình phân tích cú pháp SAX không tạo ra bất kỳ cấu trúc bên trong. Thay vào đó, nó lấy sự xuất hiện của các thành phần của tài liệu đầu vào làm sự kiện và cho khách hàng biết những gì nó đọc khi đọc qua tài liệu đầu vào. Một
Trình phân tích cú pháp DOM luôn phục vụ ứng dụng khách với toàn bộ tài liệu cho dù máy khách có thực sự cần bao nhiêu. Nhưng trình phân tích cú pháp SAX luôn phục vụ ứng dụng khách luôn chỉ với các phần của tài liệu tại bất kỳ thời điểm nào.
- Với trình phân tích cú pháp DOM, các lệnh gọi phương thức trong ứng dụng khách phải rõ ràng và tạo thành một loại chuỗi. Nhưng với SAX, một số phương thức nhất định (thường được ghi đè bởi bệnh nhân) sẽ được gọi tự động (ngầm) theo cách gọi là "gọi lại" khi một số sự kiện nhất định xảy ra. Các phương thức này không phải được khách hàng gọi một cách rõ ràng, mặc dù chúng ta có thể gọi chúng một cách rõ ràng.
Làm thế nào để chúng ta quyết định về trình phân tích cú pháp nào là tốt?
Lý tưởng nhất là một trình phân tích cú pháp tốt phải nhanh (hiệu quả về thời gian), không gian hiệu quả, giàu chức năng và dễ sử dụng. Nhưng trong thực tế, không có trình phân tích cú pháp chính nào có tất cả các tính năng này cùng một lúc. Ví dụ: Trình phân tích cú pháp DOM có nhiều chức năng (vì nó tạo một cây DOM trong bộ nhớ và cho phép bạn truy cập bất kỳ phần nào của tài liệu và cho phép bạn sửa đổi cây DOM), nhưng nó không hiệu quả khi tài liệu rất lớn và phải mất một chút thời gian để học cách làm việc với nó. Tuy nhiên, Trình phân tích SAX hiệu quả hơn về không gian trong trường hợp tài liệu đầu vào lớn (vì nó không tạo cấu trúc bên trong). Hơn thế nữa, nó chạy nhanh hơn và dễ học hơn DOM Parser vì API của nó thực sự đơn giản. Nhưng từ quan điểm chức năng, nó cung cấp ít chức năng hơn, điều đó có nghĩa là bản thân người dùng phải chăm sóc nhiều hơn, chẳng hạn như tạo cấu trúc dữ liệu của riêng họ. Nhân tiện, một trình phân tích cú pháp tốt là gì? Tôi nghĩ rằng câu trả lời thực sự phụ thuộc vào đặc điểm của ứng dụng của bạn.
Một số ứng dụng trong thế giới thực khi sử dụng trình phân tích cú pháp SAX có lợi hơn so với sử dụng trình phân tích cú pháp DOM và ngược lại? Ứng dụng thông thường cho trình phân tích cú pháp DOM và trình phân tích cú pháp SAX là gì?
Trong các trường hợp sau, sử dụng trình phân tích cú pháp SAX có lợi hơn so với sử dụng trình phân tích cú pháp DOM.
- Tài liệu đầu vào quá lớn so với bộ nhớ khả dụng (thực tế trong trường hợp này SAX là lựa chọn duy nhất của bạn)
- Bạn có thể xử lý tài liệu theo từng phần nhỏ của đầu vào. Bạn không cần toàn bộ tài liệu trước khi bạn có thể làm công việc hữu ích
- Bạn chỉ muốn sử dụng trình phân tích cú pháp để trích xuất thông tin quan tâm và tất cả tính toán của bạn sẽ hoàn toàn dựa trên cấu trúc dữ liệu do chính bạn tạo. Trên thực tế trong hầu hết các ứng dụng của chúng tôi, chúng tôi tạo các cấu trúc dữ liệu của riêng chúng thường không phức tạp như cây DOM. Từ ý nghĩa này, tôi nghĩ, cơ hội sử dụng trình phân tích cú pháp DOM ít hơn so với sử dụng trình phân tích cú pháp SAX.
Trong các trường hợp sau, sử dụng trình phân tích cú pháp DOM có lợi hơn so với sử dụng trình phân tích cú pháp SAX.
- Ứng dụng của bạn cần truy cập nhiều phần riêng biệt của tài liệu cùng một lúc.
- Ứng dụng của bạn có thể sử dụng cấu trúc dữ liệu nội bộ gần như phức tạp như chính tài liệu.
- Ứng dụng của bạn phải sửa đổi tài liệu nhiều lần.
- Ứng dụng của bạn phải lưu trữ tài liệu trong một khoảng thời gian đáng kể thông qua nhiều cuộc gọi phương thức.
Ví dụ (Sử dụng trình phân tích cú pháp DOM hoặc trình phân tích cú pháp SAX?):
Giả sử rằng một người hướng dẫn có một tài liệu XML chứa tất cả thông tin cá nhân của sinh viên cũng như những điểm mà sinh viên của anh ta đưa ra trong lớp và hiện anh ta đang chỉ định điểm cuối cho các sinh viên sử dụng một ứng dụng. Những gì anh ấy muốn sản xuất, là một danh sách với SSN và điểm số. Ngoài ra, chúng tôi giả định rằng trong ứng dụng của mình, người hướng dẫn không sử dụng cấu trúc dữ liệu như mảng để lưu trữ thông tin cá nhân của sinh viên và các điểm. Nếu người hướng dẫn quyết định đưa A cho những người kiếm được điểm trung bình hoặc cao hơn và đưa B cho những người khác, thì tốt hơn hết là bạn nên sử dụng trình phân tích cú pháp DOM trong ứng dụng của mình. Lý do là anh ta không có cách nào để biết mức trung bình của lớp là bao nhiêu trước khi toàn bộ tài liệu được xử lý. Những gì anh ấy có thể cần phải làm trong ứng dụng của mình, trước tiên là xem qua tất cả các sinh viên điểm và tính điểm trung bình, sau đó xem lại tài liệu và chỉ định điểm cuối cùng cho mỗi học sinh bằng cách so sánh điểm anh ta kiếm được với điểm trung bình của lớp. Tuy nhiên, nếu người hướng dẫn áp dụng chính sách chấm điểm như vậy mà các sinh viên đạt 90 điểm trở lên, được chỉ định A và những người khác được chỉ định B, thì có lẽ anh ta nên sử dụng trình phân tích cú pháp SAX. Lý do là, để chỉ định cho mỗi học sinh một lớp cuối cùng, anh ta không cần phải đợi toàn bộ tài liệu được xử lý. Anh ta có thể ngay lập tức chỉ định một lớp cho một học sinh sau khi trình phân tích cú pháp SAX đọc lớp của học sinh này. Trong phân tích trên, chúng tôi giả định rằng người hướng dẫn không tạo ra cấu trúc dữ liệu của riêng mình. Điều gì sẽ xảy ra nếu anh ta tạo cấu trúc dữ liệu của riêng mình, chẳng hạn như một chuỗi các chuỗi để lưu trữ SSN và một mảng các số nguyên để lấy lại các điểm? Trong trường hợp này, Tôi nghĩ SAX là một lựa chọn tốt hơn, trước khi điều này có thể tiết kiệm cả bộ nhớ và thời gian, nhưng vẫn hoàn thành công việc. Vâng, một xem xét thêm về ví dụ này. Điều gì sẽ xảy ra nếu những gì người hướng dẫn muốn làm không phải là in một danh sách, mà là để lưu lại tài liệu gốc với điểm của mỗi sinh viên được cập nhật? Trong trường hợp này, trình phân tích cú pháp DOM sẽ là lựa chọn tốt hơn cho dù anh ta đang áp dụng chính sách phân loại nào. Anh ta không cần phải tạo bất kỳ cấu trúc dữ liệu nào của riêng mình. Những gì anh ta cần làm là trước tiên sửa đổi cây DOM (nghĩa là đặt giá trị thành nút 'lớp') và sau đó lưu toàn bộ cây đã sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc. chưa hoàn thành công việc Vâng, một xem xét thêm về ví dụ này. Điều gì sẽ xảy ra nếu những gì người hướng dẫn muốn làm không phải là in một danh sách, mà là để lưu lại tài liệu gốc với điểm của mỗi sinh viên được cập nhật? Trong trường hợp này, trình phân tích cú pháp DOM sẽ là lựa chọn tốt hơn cho dù anh ta đang áp dụng chính sách phân loại nào. Anh ta không cần phải tạo bất kỳ cấu trúc dữ liệu nào của riêng mình. Những gì anh ta cần làm là trước tiên sửa đổi cây DOM (nghĩa là đặt giá trị thành nút 'lớp') và sau đó lưu toàn bộ cây đã sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc. chưa hoàn thành công việc Vâng, một xem xét thêm về ví dụ này. Điều gì sẽ xảy ra nếu những gì người hướng dẫn muốn làm không phải là in một danh sách, mà là để lưu lại tài liệu gốc với điểm của mỗi sinh viên được cập nhật? Trong trường hợp này, trình phân tích cú pháp DOM sẽ là lựa chọn tốt hơn cho dù anh ta đang áp dụng chính sách phân loại nào. Anh ta không cần phải tạo bất kỳ cấu trúc dữ liệu nào của riêng mình. Những gì anh ta cần làm là trước tiên sửa đổi cây DOM (nghĩa là đặt giá trị thành nút 'lớp') và sau đó lưu toàn bộ cây đã sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc. Nhưng để lưu tài liệu gốc trở lại với điểm của từng học sinh được cập nhật? Trong trường hợp này, trình phân tích cú pháp DOM sẽ là lựa chọn tốt hơn cho dù anh ta đang áp dụng chính sách phân loại nào. Anh ta không cần phải tạo bất kỳ cấu trúc dữ liệu nào của riêng mình. Những gì anh ta cần làm là trước tiên sửa đổi cây DOM (nghĩa là đặt giá trị thành nút 'lớp') và sau đó lưu toàn bộ cây đã sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc. Nhưng để lưu tài liệu gốc trở lại với điểm của từng học sinh được cập nhật? Trong trường hợp này, trình phân tích cú pháp DOM sẽ là lựa chọn tốt hơn cho dù anh ta đang áp dụng chính sách phân loại nào. Anh ta không cần phải tạo bất kỳ cấu trúc dữ liệu nào của riêng mình. Những gì anh ta cần làm là trước tiên sửa đổi cây DOM (nghĩa là đặt giá trị thành nút 'lớp') và sau đó lưu toàn bộ cây đã sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc. nút) và sau đó lưu toàn bộ cây sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc. nút) và sau đó lưu toàn bộ cây sửa đổi. Nếu anh ta chọn sử dụng trình phân tích cú pháp SAX thay vì trình phân tích cú pháp DOM, thì trong trường hợp này anh ta phải tạo một cấu trúc dữ liệu gần như phức tạp như một cây DOM trước khi anh ta có thể hoàn thành công việc.
Một ví dụ
Báo cáo sự cố : Viết chương trình Java để trích xuất tất cả thông tin về các vòng tròn là các thành phần trong tài liệu XML đã cho. Chúng tôi giả định rằng mỗi phần tử vòng tròn có ba phần tử con (nghĩa là x, y và bán kính) cũng như một thuộc tính màu. Một tài liệu mẫu được đưa ra dưới đây:
<?xml version="1.0"?>
<!DOCTYPE shapes [
<!ELEMENT shapes (circle)*>
<!ELEMENT circle (x,y,radius)>
<!ELEMENT x (#PCDATA)>
<!ELEMENT y (#PCDATA)>
<!ELEMENT radius (#PCDATA)>
<!ATTLIST circle color CDATA #IMPLIED>
]>
<shapes>
<circle color="BLUE">
<x>20</x>
<y>20</y>
<radius>20</radius>
</circle>
<circle color="RED" >
<x>40</x>
<y>40</y>
<radius>20</radius>
</circle>
</shapes>
Chương trình với DOMparser
import java.io.*;
import org.w3c.dom.*;
import org.apache.xerces.parsers.DOMParser;
public class shapes_DOM {
static int numberOfCircles = 0; // total number of circles seen
static int x[] = new int[1000]; // X-coordinates of the centers
static int y[] = new int[1000]; // Y-coordinates of the centers
static int r[] = new int[1000]; // radius of the circle
static String color[] = new String[1000]; // colors of the circles
public static void main(String[] args) {
try{
// create a DOMParser
DOMParser parser=new DOMParser();
parser.parse(args[0]);
// get the DOM Document object
Document doc=parser.getDocument();
// get all the circle nodes
NodeList nodelist = doc.getElementsByTagName("circle");
numberOfCircles = nodelist.getLength();
// retrieve all info about the circles
for(int i=0; i<nodelist.getLength(); i++) {
// get one circle node
Node node = nodelist.item(i);
// get the color attribute
NamedNodeMap attrs = node.getAttributes();
if(attrs.getLength() > 0)
color[i]=(String)attrs.getNamedItem("color").getNodeValue();
// get the child nodes of a circle node
NodeList childnodelist = node.getChildNodes();
// get the x and y value
for(int j=0; j<childnodelist.getLength(); j++) {
Node childnode = childnodelist.item(j);
Node textnode = childnode.getFirstChild();//the only text node
String childnodename=childnode.getNodeName();
if(childnodename.equals("x"))
x[i]= Integer.parseInt(textnode.getNodeValue().trim());
else if(childnodename.equals("y"))
y[i]= Integer.parseInt(textnode.getNodeValue().trim());
else if(childnodename.equals("radius"))
r[i]= Integer.parseInt(textnode.getNodeValue().trim());
}
}
// print the result
System.out.println("circles="+numberOfCircles);
for(int i=0;i<numberOfCircles;i++) {
String line="";
line=line+"(x="+x[i]+",y="+y[i]+",r="+r[i]+",color="+color[i]+")";
System.out.println(line);
}
} catch (Exception e) {e.printStackTrace(System.err);}
}
}
Chương trình với SAXparser
import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.apache.xerces.parsers.SAXParser;
public class shapes_SAX extends DefaultHandler {
static int numberOfCircles = 0; // total number of circles seen
static int x[] = new int[1000]; // X-coordinates of the centers
static int y[] = new int[1000]; // Y-coordinates of the centers
static int r[] = new int[1000]; // radius of the circle
static String color[] = new String[1000]; // colors of the circles
static int flagX=0; //to remember what element has occurred
static int flagY=0; //to remember what element has occurred
static int flagR=0; //to remember what element has occurred
// main method
public static void main(String[] args) {
try{
shapes_SAX SAXHandler = new shapes_SAX (); // an instance of this class
SAXParser parser=new SAXParser(); // create a SAXParser object
parser.setContentHandler(SAXHandler); // register with the ContentHandler
parser.parse(args[0]);
} catch (Exception e) {e.printStackTrace(System.err);} // catch exeptions
}
// override the startElement() method
public void startElement(String uri, String localName,
String rawName, Attributes attributes) {
if(rawName.equals("circle")) // if a circle element is seen
color[numberOfCircles]=attributes.getValue("color"); // get the color attribute
else if(rawName.equals("x")) // if a x element is seen set the flag as 1
flagX=1;
else if(rawName.equals("y")) // if a y element is seen set the flag as 2
flagY=1;
else if(rawName.equals("radius")) // if a radius element is seen set the flag as 3
flagR=1;
}
// override the endElement() method
public void endElement(String uri, String localName, String rawName) {
// in this example we do not need to do anything else here
if(rawName.equals("circle")) // if a circle element is ended
numberOfCircles += 1; // increment the counter
}
// override the characters() method
public void characters(char characters[], int start, int length) {
String characterData =
(new String(characters,start,length)).trim(); // get the text
if(flagX==1) { // indicate this text is for <x> element
x[numberOfCircles] = Integer.parseInt(characterData);
flagX=0;
}
else if(flagY==1) { // indicate this text is for <y> element
y[numberOfCircles] = Integer.parseInt(characterData);
flagY=0;
}
else if(flagR==1) { // indicate this text is for <radius> element
r[numberOfCircles] = Integer.parseInt(characterData);
flagR=0;
}
}
// override the endDocument() method
public void endDocument() {
// when the end of document is seen, just print the circle info
System.out.println("circles="+numberOfCircles);
for(int i=0;i<numberOfCircles;i++) {
String line="";
line=line+"(x="+x[i]+",y="+y[i]+",r="+r[i]+",color="+color[i]+")";
System.out.println(line);
}
}
}