Cách đọc và ghi tệp XML bằng Java
Các tệp XML có thể phục vụ nhiều mục đích khác nhau, bao gồm cả việc lưu trữ dữ liệu. Trước khi JSON trở nên phổ biến, XML là định dạng ưa thích để biểu diễn, lưu trữ và truyền dữ liệu có cấu trúc.
Mặc dù mức độ phổ biến của nó đã giảm đi trong thời hiện đại, thỉnh thoảng người ta vẫn có thể bắt gặp XML. Do đó, điều quan trọng là phải đạt được trình độ thành thạo khi làm việc với định dạng này. Khám phá sự phức tạp của việc sử dụng Giao diện lập trình ứng dụng (API) Mô hình đối tượng tài liệu (DOM) để đọc và ghi tài liệu XML bằng cách sử dụng Java làm công cụ chính của bạn.
Yêu cầu xử lý XML trong Java
Phiên bản tiêu chuẩn Java, hay SE, bao gồm API Java để xử lý XML, thường được gọi là JAXP, một khung toàn diện bao gồm nhiều khía cạnh khác nhau của việc xử lý XML. Tiết mục mở rộng này bao gồm một số thành phần không thể thiếu, bao gồm:
Mô hình đối tượng tài liệu (DOM) bao gồm một tập hợp các lớp tạo điều kiện thuận lợi cho việc thao tác các thành phần XML, bao gồm các phần tử, nút và thuộc tính. Tuy nhiên, do thiết kế của nó để tải toàn bộ tệp XML vào bộ nhớ để xử lý, nên nó không được trang bị lý tưởng để xử lý các tài liệu có kích thước đáng kể một cách hiệu quả.
API đơn giản cho XML (SAX) là một cơ chế phân tích cú pháp nhẹ được thiết kế đặc biệt để xử lý các tài liệu XML. Không giống như Mô hình đối tượng tài liệu (DOM), xây dựng toàn bộ cấu trúc cây trong quá trình xử lý, SAX hoạt động bằng cách kích hoạt các sự kiện dựa trên nội dung XML gặp phải khi phân tích cú pháp tệp. Cách tiếp cận này làm giảm mức tiêu thụ bộ nhớ và cho phép linh hoạt hơn trong việc xử lý lượng lớn dữ liệu. Tuy nhiên, việc sử dụng SAX có thể hơi khó khăn so với việc sử dụng DOM do nó phụ thuộc vào các mô hình lập trình dựa trên sự kiện.
StAX, hay API phát trực tuyến cho XML, thể hiện sự bổ sung gần đây hơn cho lĩnh vực xử lý XML. Tự hào với khả năng thực hiện ấn tượng về lọc, xử lý và sửa đổi luồng, công cụ mạnh mẽ này quản lý để đạt được mục tiêu của mình mà không cần tải toàn bộ tài liệu XML vào bộ nhớ. Ngược lại với kiến trúc hướng sự kiện được API SAX ưa chuộng, StAX sử dụng cách tiếp cận kiểu kéo giúp đơn giản hơn và thân thiện hơn với người dùng khi nói đến mã hóa.
Để xử lý dữ liệu XML trong một ứng dụng Java, cần phải kết hợp một số gói nhất định để hỗ trợ chức năng này. Các gói này cung cấp nhiều phương thức và lớp khác nhau để phân tích cú pháp, thao tác và tạo tài liệu XML.
import javax.xml.parsers.*;
import javax.xml.transform.*;
import org.w3c.dom.*;
Chuẩn bị một tệp XML mẫu
Để hiểu mã mẫu và các khái niệm đằng sau nó, hãy sử dụng mẫu này Tệp XML của Microsoft. Đây là một đoạn trích:
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
...snipped...
Đọc tệp XML bằng API DOM
Để xử lý hiệu quả một tệp XML bằng cách sử dụng Giao diện lập trình ứng dụng Mô hình đối tượng tài liệu (DOM), trước tiên chúng ta phải tạo một phiên bản của lớp DocumentBuilder
, lớp này sẽ tạo điều kiện thuận lợi cho việc phân tích cú pháp tài liệu XML.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Bây giờ người ta có thể chọn lưu trữ toàn bộ tài liệu trong bộ nhớ của mình, bắt đầu bằng phần tử gốc XML, trong trường hợp này tương ứng với phần tử “danh mục”.
// XML file to read
File file = "<path_to_file>";
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();
Thật vậy, bằng cách sử dụng phương pháp này, người ta có được quyền truy cập hoàn toàn vào toàn bộ tài liệu XML, bắt đầu với nút chính của nó, cụ thể là phần tử “danh mục”.
Trích xuất thông tin bằng API DOM
Khi bạn đã lấy được phần tử gốc bằng trình phân tích cú pháp XML, bạn có thể sử dụng API Mô hình đối tượng tài liệu (DOM) để truy cập các phần thông tin có giá trị bên trong nó. Ví dụ: truy xuất tất cả các phần tử con trực tiếp của phần tử gốc và lặp qua chúng là một cách tiếp cận thực tế. Tuy nhiên, xin lưu ý rằng phương thức getChildNodes()
trả về tất cả các loại nút con, bao gồm nút văn bản và nút nhận xét, không liên quan đến nhiệm vụ hiện tại của chúng ta. Do đó, chúng ta chỉ nên nhắm mục tiêu cụ thể vào các phần tử con khi xử lý các kết quả này.
NodeList books = catalog.getChildNodes();
for (int i = 0, ii = 0, n = books.getLength() ; i < n ; i\+\+) {
Node child = books.item(i);
if ( child.getNodeType() != Node.ELEMENT_NODE )
continue;
Element book = (Element)child;
// work with the book Element here
}
Để định vị một phần tử con cụ thể dưới phần tử cha của nó trong tài liệu XML bằng C#, người ta có thể tạo một phương thức tĩnh lặp lại tập hợp các nút con để xác định phần tử mong muốn dựa trên tên của nó. Nếu phần tử được phát hiện trong quá trình này, nó sẽ được trả về; mặt khác, null đại diện cho kết quả.
static private Node findFirstNamedElement(Node parent,String tagName)
{
NodeList children = parent.getChildNodes();
for (int i = 0, in = children.getLength() ; i < in ; i\+\+) {
Node child = children.item(i);
if (child.getNodeType() != Node.ELEMENT_NODE)
continue;
if (child.getNodeName().equals(tagName))
return child;
}
return null;
}
Xin lưu ý rằng API DOM phân loại nội dung văn bản được bao trong một phần tử dưới dạng một nút riêng lẻ thuộc danh mục TEXT\_NODE. Nội dung văn bản có thể bao gồm một số nút văn bản liền kề, cần phải xử lý cụ thể để truy xuất văn bản của một phần tử nhất định:
static private String getCharacterData(Node parent)
{
StringBuilder text = new StringBuilder();
if ( parent == null )
return text.toString();
NodeList children = parent.getChildNodes();
for (int k = 0, kn = children.getLength() ; k < kn ; k\+\+) {
Node child = children.item(k);
if (child.getNodeType() != Node.TEXT_NODE)
break;
text.append(child.getNodeValue());
}
return text.toString();
}
Bằng cách sử dụng các hàm tiện ích được cung cấp, chúng ta hãy xem xét một ví dụ trích xuất dữ liệu thích hợp từ một tài liệu XML thể hiện một danh mục sách. Mã tiếp theo hiển thị chi tiết toàn diện về từng ấn phẩm riêng lẻ có trong danh mục nói trên:
NodeList books = catalog.getChildNodes();
for (int i = 0, ii = 0, n = books.getLength() ; i < n ; i\+\+) {
Node child = books.item(i);
if (child.getNodeType() != Node.ELEMENT_NODE)
continue;
Element book = (Element)child;
ii\+\+;
String id = book.getAttribute("id");
String author = getCharacterData(findFirstNamedElement(child, "author"));
String title = getCharacterData(findFirstNamedElement(child, "title"));
String genre = getCharacterData(findFirstNamedElement(child, "genre"));
String price = getCharacterData(findFirstNamedElement(child, "price"));
String pubdate = getCharacterData(findFirstNamedElement(child, "pubdate"));
String descr = getCharacterData(findFirstNamedElement(child, "description"));
System.out.printf("%3d. book id = %s\n" \+
" author: %s\n" \+
" title: %s\n" \+
" genre: %s\n" \+
" price: %s\n" \+
" pubdate: %s\n" \+
" descr: %s\n",
ii, id, author, title, genre, price, pubdate, descr);
}
Dưới đây là giải thích từng bước về mã:
Mã này nghiên cứu các thành phần con của đàn bố mẹ, hoặc thành phần gốc, đóng vai trò là nền tảng cho toàn bộ cấu trúc.
Đối với mỗi nút con riêng lẻ, tương ứng với một cuốn sách cụ thể, chương trình sẽ xác minh xem cấu trúc dữ liệu cơ bản của nó có đặc tính là ELEMENT\_NODE hay không. Trong trường hợp điều kiện tiên quyết này không được đáp ứng, quá trình sẽ chuyển sang lần lặp tiếp theo.
Nếu gặp một nút con thuộc loại ELEMENT\_NODE trong quá trình duyệt cây DOM, thuộc tính con
sẽ bị ép buộc vào một thể hiện của giao diện Element
.
Việc thực hiện chương trình tiếp theo bao gồm việc trích xuất nhiều thuộc tính và dữ liệu ký tự liên quan đến một thành phần sách được chỉ định, chẳng hạn như mã định danh duy nhất (“id”), tên tác giả, tựa đề, thể loại, giá, ngày xuất bản và thông tin mô tả. Thông tin được trích xuất này sau đó được in ra bảng điều khiển bằng cách sử dụng phương thức System.out.printf() cho mục đích trình bày.
Đây là kết quả đầu ra trông như thế nào:
Viết đầu ra XML bằng API chuyển đổi
Java cung cấp API chuyển đổi XML như một phương tiện để thao tác dữ liệu XML. API này được sử dụng cùng với việc chuyển đổi danh tính để tạo đầu ra. Để minh họa, hãy xem xét việc bổ sung danh mục mẫu trước đó bằng cách kết hợp phần tử sách mới.
Thông tin liên quan đến tác phẩm văn học, bao gồm tác giả và tiêu đề, có thể được lấy từ một nguồn không liên quan như cơ sở dữ liệu hoặc tệp thuộc tính. Tệp thuộc tính được cung cấp đóng vai trò là mô hình cho mục đích này.
id=bk113
author=Jane Austen
title=Pride and Prejudice
genre=Romance
price=6.99
publish_date=2010-04-01
description="It is a truth universally acknowledged, that a single man in possession of a good fortune must be in want of a wife." So begins Pride and Prejudice, Jane Austen's witty comedy of manners-one of the most popular novels of all time-that features splendidly civilized sparring between the proud Mr. Darcy and the prejudiced Elizabeth Bennet as they play out their spirited courtship in a series of eighteenth-century drawing-room intrigues.
Để xử lý một tài liệu XML, cần phải sử dụng kỹ thuật phân tích cú pháp đã nêu trước đó. Điều này liên quan đến việc chia nhỏ cấu trúc dựa trên văn bản của tệp và trích xuất thông tin liên quan để phân tích hoặc thao tác thêm.
File file = ...; // XML file to read
Document document = builder.parse(file);
Element catalog = document.getDocumentElement();
Bằng cách sử dụng lớp Thuộc tính được cung cấp trong ngôn ngữ lập trình Java, người ta có thể truy xuất và xử lý thông tin được lưu trữ trong một tệp cấu hình bên ngoài riêng biệt được gọi là tệp “thuộc tính”. Quá trình này bao gồm độ phức tạp mã hóa tối thiểu, hợp lý hóa việc tích hợp các tùy chọn hoặc cài đặt do người dùng xác định với logic ứng dụng tổng thể.
String propsFile = "<path_to_file>";
Properties props = new Properties();
try (FileReader in = new FileReader(propsFile)) {
props.load(in);
}
Khi tải tệp thuộc tính, người ta có thể trích xuất các giá trị mong muốn để bổ sung từ tệp đã nói.
String id = props.getProperty("id");
String author = props.getProperty("author");
String title = props.getProperty("title");
String genre = props.getProperty("genre");
String price = props.getProperty("price");
String publish_date = props.getProperty("publish_date");
String descr = props.getProperty("description");
Bây giờ, hãy tạo một phần tử sách trống.
Element book = document.createElement("book");
book.setAttribute("id", id);
Việc kết hợp các thành phần riêng lẻ của cuốn sách vào kho văn bản thể hiện bản thân nó như một nỗ lực không phức tạp. Để tạo thuận lợi cho quá trình này, người ta có thể biên soạn một danh mục các tên gọi cần thiết bằng cách sắp xếp chúng thành một bộ sưu tập được gọi là “Danh sách”. Bằng cách thực hiện một thao tác lặp lại trong danh sách này, các mục tương ứng có thể được thêm vào khung tường thuật rộng hơn một cách hiệu quả.
List<String> elnames =Arrays.asList("author", "title", "genre", "price",
"publish_date", "description");
for (String elname : elnames) {
Element el = document.createElement(elname);
Text text = document.createTextNode(props.getProperty(elname));
el.appendChild(text);
book.appendChild(el);
}
catalog.appendChild(book);
Thành phần danh mục nói trên hiện đang sở hữu một thực thể sách bổ sung mới được giới thiệu gần đây. Nhiệm vụ duy nhất còn lại là xây dựng tài liệu Ngôn ngữ đánh dấu mở rộng (XML) đã sửa đổi bao gồm các bản cập nhật nói trên.
Để tạo một tài liệu XML bằng cách sử dụng máy biến áp, trước tiên người ta phải có bản sao của máy biến áp nói trên. Điều này có thể được thực hiện bằng cách triển khai mã cần thiết trong ngôn ngữ lập trình hoặc môi trường phát triển. Ví dụ: trong Python, người ta có thể sử dụng thư viện transformers
và các hàm liên quan của nó để xây dựng một phiên bản của Transformers để sử dụng với các tác vụ xử lý ngôn ngữ tự nhiên.
TransformerFactory tfact = TransformerFactory.newInstance();
Transformer tform = tfact.newTransformer();
tform.setOutputProperty(OutputKeys.INDENT, "yes");
tform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
Bạn có thể sử dụng phương thức setOutputProperty()
để chỉ định mức thụt lề mong muốn trong đầu ra được tạo.
Giai đoạn cuối cùng liên quan đến việc thực hiện quá trình chuyển đổi. Kết quả được hiển thị thông qua luồng đầu ra, có thể được quan sát bằng cách giám sát bảng điều khiển hoặc thiết bị đầu cuối nơi chương trình đang chạy.
tform.transform(new DOMSource(document), new StreamResult(System.out));
Để lưu đầu ra của chương trình vào một tệp thay vì in nó ra bàn điều khiển, người ta có thể sử dụng phương pháp sau:
tform.transform(new DOMSource(document), new StreamResult(new File("output.xml")));
Để thực hiện cả việc đọc và ghi các tệp Ngôn ngữ đánh dấu mở rộng (XML) bằng ngôn ngữ lập trình Java, một loạt hành động thủ tục phải được thực hiện tuần tự. Chúng bao gồm việc xác định một đối tượng tài liệu XML, tạo các nút trong tài liệu đó, gắn các phần tử con vào các nút cha, chỉ định các thuộc tính phần tử, nối thêm hoặc chèn các nút mới vào các vị trí khác nhau trong tài liệu và cuối cùng đóng mọi thẻ đang mở trước khi kết thúc quá trình.
Bây giờ bạn đã biết cách đọc và ghi tệp XML bằng Java
Sử dụng Java để phân tích và thao tác Ngôn ngữ đánh dấu mở rộng (XML) là một kỹ năng không thể thiếu thường gặp trong các ứng dụng thực tế. Mô hình đối tượng tài liệu (DOM) và API chuyển đổi đặc biệt mang lại lợi ích cho mục đích này.
Việc nắm bắt toàn diện về Mô hình đối tượng tài liệu (DOM) là điều không thể thiếu đối với các nhà phát triển đang tìm cách tạo tập lệnh phía máy khách cho các ứng dụng hoặc trang web dựa trên web. May mắn thay, kiến trúc của DOM được chuẩn hóa trên nhiều ngôn ngữ lập trình khác nhau, cho phép thao tác nhất quán thông qua mã được viết bằng các ngôn ngữ như Java và JavaScript.