Tóm tắt: Phần 1 của loạt bài này đã đi qua việc đọc các tệp
Microsoft® Excel® bằng cách sử dụng công nghệ Java™ và Apache
POI. Nhưng việc đọc các tệp Excel chỉ là sự khởi đầu. Phần đăng này trộn lẫn
Excel và XML để làm giảm đau đầu cho các nhà phát triển, nhưng người bật đèn
xanh cho ý tưởng về chuyển đổi giữa các định dạng báo cáo.
Bạn có kiểm tra lý do sáng tạo #432 cho ông chủ của bạn trong tuần này
không? Bạn có bị cám dỗ không?
Hy vọng là không. Phần 1 của loạt bài này đã giúp bạn hiểu cách làm việc
với các bảng tính Microsoft® Excel® bằng công nghệ
Java™ xem liên kết đến bài "Đọc, phục hồi và sử dụng
lại: Báo cáo được tạo dễ dàng bắng Excel, XML, và các công nghệ Java, Phần
1" (trong phần Tài nguyên). Chỉ cần tải về Apache
POI và cài đặt sử dụng nó, và không chậm trễ, đi qua một bảng tính Excel
hầu như cũng dễ dàng như đi bộ qua công viên. Và hầu như toàn đèn xanh.
Nhưng việc đọc các tệp Excel chỉ là sự khởi đầu. Phần đăng này cho bạn thấy
cách sử dụng Apache POI và XOM (XML Object Model - Mô hình đối tượng XML)
để lưu trữ một tệp Excel trong các đối tượng XML. Sau đó, bạn có thể phục
hồi các đối tượng đó để viết một bảng tính Excel và tệp XML hoàn toàn
mới.
Ứng dụng mẫu
Ứng dụng mẫu có chứa một bảng tính Excel có tên là Employee_List.xls
từ Tổng công ty Điện lực Planet hư cấu. Ông chủ Lớn đã thuyết phục các
nhân viên hàng đầu của Điện lực Planet (Planet Power) tặng 1% tổng số tiền
lương của họ cho mục đích yêu thích của mình: Khu bảo tồn liên hành tinh
loài chuột đồng hoang dã to lớn có sử dụng kỹ thuật di truyền học (GEE
WHIS). Ứng dụng mẫu tính toán số lượng và tạo ra một báo cáo XML để gửi
gấp tới khu bảo tồn này. Trong khi đó, ứng dụng này viết một bảng tính
Excel cho Ông chủ Lớn.
Để làm theo theo các ví dụ trong bài viết này, hãy tải
về các ví dụ mẫu và trích xuất các tệp vào C:\Planet Power. Rồi,
khởi động Eclipse.
Dự án Employees2
Eclipse
Để nhập khẩu dự án Eclipse Employees2 chứa các ứng dụng mẫu, hãy thực hiện
các bước sau:
- Trong Eclipse, nhấn chuột phải vào Package Explorer (Trình thám hiểm gói) và sau đó nhấn Import (Nhập khẩu).
- Mở rộng General, rồi chọn Existing Projects into Workspace (Các
dự án hiện có vào trong Vùng làm việc). Nhấn Next (Hình 1).
Hình 1. Đặt một dự án hiện có vào trong vùng làm việc
- Nhấn Browse (Duyệt) bên cạnh mục Select root directory (Chọn thư mục gốc), rồi chuyển hướng đến C:\Planet Power\Employees2.
- Chọn thư mục Employees2, nhấn OK, sau đó nhấn Finish (Hình 2).
Hình 2. Kết thúc nhập khẩu một dự án vào Eclipse
Thư mục Employees2 sẽ xuất hiện trong ô Package Explorer.
Lưu ý: Với dự án này, sử dụng tệp ExcelXML.java trong dự án
Employees2 trong thư mục src\default_package.
Bắt đầu
Trong Phần 1 của loạt bài này, bước đầu tiên của bạn là nhập khẩu Apache
POI cùng với các lớp xử lý ngoại lệ và các lớp xử lý tệp (xem phần Tài nguyên để có liên kết đến Phần 1). Ngoài ra,
bạn sẽ cần thêm một số lớp XML API cùng với các lớp để làm việc với các
số, như trong Liệt kê 1.
Liệt kê 1. Nhập khẩu các lớp (ExcelXML.java)
// File and exception handling imports import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.text.ParseException; // Apache POI imports import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFHyperlink; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFDataFormatter; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFDataFormat; // XOM imports import nu.xom.Document; import nu.xom.Elements; import nu.xom.Element; import nu.xom.Attribute; import nu.xom.Serializer; // Imports for later calculations import java.text.NumberFormat; |
Sau khi nhập khẩu các lớp, bạn đã sẵn sàng bắt đầu lập trình bên trong
main()
.
Mã trong Liệt kê 2 mô tả một cấu trúc xử lý-ngoại lệ
cho tệp (
IOException
) và các lỗi chuyển đổi số
(ParseException
). Liệt kê 2. Thiết lập xử lý ngoại lệ (ExcelXML.java)
public class ExcelXML { public static void main(String[] args) { try { // Put file and number handling code here. // End try } catch (IOException e) { System.out.println("File Input/Output Exception!"); } catch (ParseException e) { System.out.println("Number Parse Exception!"); } // End main method } // End class block } |
Bây giờ bạn có thể bắt đầu làm việc với XOM.
Các đối tượng XOM, Document, và
XML
XOM đơn giản hóa việc thực hiện với XML bằng cách phân tích cú pháp nó
thành các đối tượng mô tả các đoạn của một tài liệu XML. Lớp mô tả toàn bộ
một tài liệu XML là
nu.xom.Document
. Từ
Document
này, bạn có thể thêm hoặc truy cập vào
các đoạn khác. Một số lớp để làm việc với XML gồm: nu.xom.Builder
nu.xom.Document
nu.xom.Elements
nu.xom.Element
nu.xom.Serializer
Các đoạn XML chính được gọi là elements. Một phần tử có một cặp các
thẻ và nội dung ở giữa. Dưới đây là một ví dụ của một phần tử từ một tệp
mẫu có tên là weather_service.xml.
<dawn>07:00</dawn> |
Các từ dawn và các dấu ngoặc (
<>
)
và dấu gạch chéo (/
) của chúng được gọi là
các thẻ.
07:00 là nội dung.
Các phần tử có thể chứa các phần tử khác, nội dung văn bản, hoặc cả hai.
Một phần tử chứa được gọi là phần tử cha mẹ, và các phần tử bên
trong được gọi là các phần tử con hoặc trẻ em.
Trong XOM, các phần tử được mô tả bởi các đối tượng
nu.xom.Element
. Một tập các phần tử là một đối
tượng nu.xom.Elements
.
Gốc của mọi điều tốt
Để tìm các phần tử, hãy sử dụng một phần tử mà chắc chắn mọi tài liệu XML
đúng định dạng đều phải có: phần tử gốc. Phần tử gốc làm một thùng
chứa cho mọi phần tử khác. Nếu một tài liệu không có một phần tử gốc
(root), nó không phải là XML đúng dạng.
Để tìm phần tử gốc, sử dụng
getRootElement()
trên đối tượng Document
. Việc nhận đúng một
phần tử mở ra các chiến lược để khai thác tài liệu. Bạn có muốn làm việc
với phần tử con của phần tử gốc không? Hãy nhận một danh sách tất cả các
phần tử con hoặc các phần tử con có một tên cụ thể bằng cách sử dụng các
biến của Element.getChildElements()
. Bạn có cần
một phần tử duy nhất không? Chỉ nhận phần tử con đầu tiên có một tên cụ
thể từ Element.getFirstChildElement()
.
Điều này có vẻ quen thuộc? Lặp qua hết một tài liệu XML để tìm phần tử con
của phần tử con cũng giống như việc lặp qua hết một bảng tính Excel.
Trong bài trước, bạn đã thấy cách phương thức
getStringCellValue()
của Apache POI đã lấy ra
một giá chuỗi trị từ một HSSFCell
của Excel.
Tương tự như vậy, XOM sử dụng
Element.getValue()
để nhận được nội dung chuỗi
từ một phần tử XML. Tuy nhiên, không giống như làm việc với các ô bảng
tính, làm việc với các phần tử XOM không đòi hỏi kiểm tra các kiểu dữ
liệu. Tất cả XML là toàn văn bản.
Ứng dụng hỗn hợp Excel-XML và đánh dấu
XML
Khi xem xét kỹ các bảng tính và trích xuất dữ liệu, bạn có thể lưu trữ dữ
liệu trong một số các đối tượng bất kỳ: Các chuỗi (Strings). Các mảng
(Arrays). Các lồng sóc (Squirrels). Bài viết này sử dụng các phần tử XML.
(Cách đó, dễ dàng hơn trên Squirrels). Bài viết này cũng mang đến một số
cái nhìn sâu hơn về biên dịch giữa hai định dạng.
Liệt kê 3 chuẩn bị một
HSSFWorkbook
và một
Document
mới để lưu trữ thông tin của bảng
tính.Liệt kê 3. Chuẩn bị một bảng tính và XML Document (ExcelXML.java)
// First, create a new XML Document object to load the Excel sheet into XML. // To create an XML Document, first create an element to be its root. Element reportRoot = new Element("sheet"); // Create a new XML Document object using the root element Document XMLReport = new Document(reportRoot); // Set up a FileInputStream to represent the Excel spreadsheet FileInputStream excelFIS = new FileInputStream("C:\\Planet Power\\Employee_List.xls"); // Create an Excel Workbook object using the FileInputStream created above HSSFWorkbook excelWB = new HSSFWorkbook(excelFIS); |
Có một cái gì đó có vẻ ngược với Liệt kê 3 phải không?
Mã tạo một
Element
mới trước khi tạo một
Document
. Tại sao?
Một
Document
mới toanh đòi hỏi một
Element
là phần tử gốc của nó. Hãy nhớ rằng
Document
không thể mô tả một tài liệu XML đúng
định dạng mà không có một phần tử gốc. Vì vậy, bạn phải chuyển cho nó một
phần tử gốc khi bạn tạo nó.
Ngược lại, bạn có thể tạo ra các phần tử không thuộc về một phần tử gốc.
Bạn có thể để mặc chúng như là các phần tử trôi nổi tự do hoặc gắn thêm
chúng vào một phần tử gốc tài liệu hoặc các phần tử khác. Tuy nhiên, trước
khi bạn bắt đầu, bạn nên có kế hoạch cấu trúc XML của mình.
Cấu trúc XML
XML mô tả và định dạng dữ liệu, chủ yếu thông qua các phần tử và các thuộc
tính. Các thuộc tính là các cặp tên-giá trị. Một phần tên của một
thuộc tính mô tả các dữ liệu mà thuộc tính lưu giữ. Giá trị của thuộc tính
là dữ liệu của nó. Những người viết mã HTML đã quen thuộc với các thuộc
tính như:
<font size="12">Hello, Planet!</font> |
Toàn bộ liệt kê này là một phần tử. Thẻ là
<font>
. Thuộc tính là
size="12"
. Tên của thuộc tính là
size
(kích thước). Giá trị của nó là
12
. Dấu gán (=
) nối
cả hai. Nói chung, dữ liệu được lưu trữ trong các phần tử, trong khi siêu
dữ liệu được lưu trữ trong các thuộc tính.
Vì vậy, làm thế nào để bảng tính Employee_List.xls sẽ chuyển dịch sang
XMLL?
Phương thức đánh dấu 1: bắt chước cấu
trúc bảng tính
Một cách để cấu trúc XML là bắt chước cấu trúc vật lý của một bảng tính
Excel. Hãy xem Liệt kê 4.
Liệt kê 4. Cấu trúc XML dựa trên cấu trúc bảng tính chung
<sheet> <row> <cell> Thumb </cell> <cell> Green </cell> <cell> Growing Plants </cell> <cell> 5:00 AM </cell> <cell> 2:00 PM </cell> <cell> 150,000 </cell> </row> </sheet> |
Với định dạng này, các hàng và các ô đều rõ ràng. Nhưng mỗi ô lưu giữ dữ
liệu nào? Thời gian bắt đầu hay kết thúc của nhân viên là 5:00 AM phải
không? Khả năng sử dụng tên cột như một thuộc tính của ô thế nào?
Nếu việc duy trì cấu trúc bảng tính Excel là quan trọng, thì liệt kê đó có
thể làm việc. Tuy nhiên, trong XOM, không cần học cách viết các truy vấn
XPath, không có phương thức dễ dàng nào để trích xuất một bộ sưu tập các
phần tử dựa trên các giá trị thuộc tính của chúng. Với các tên cột được
lưu trữ như các thuộc tính, mã bổ sung cần thiết để xác định vị trí một bộ
sưu tập của tất cả các phần tử từ một cột cụ thể. Vì XOM chứa một phương
thức để tìm các phần tử theo tên của chúng, hãy xem xét việc sử dụng các
cột Excel như các tên phần tử chứ không phải như các thuộc tính.
Phương thức đánh dấu 2: bắt chước cấu
trúc dữ liệu
Thay vì XML dựa vào cách trình bày của dữ liệu, hãy xem xét một cấu trúc mô
tả dữ liệu. Đó là những gì mà XML làm tốt nhất, như Liệt
kê 5 cho thấy.
Liệt kê 5. Cấu trúc XML dựa trên dữ liệu mô tả
<EmployeeData> <Employee> <EmployeeLastName> Thumb </EmployeeLastName> <EmployeeFirstName> Green </EmployeeFirstName> <MainSuperPower> Growing Plants </MainSuperPower> <DailyStartTime> 5:00 AM </DailyStartTime> <DailyEndTime> 2:00 PM </DailyEndTime> <Salary> 150,000 </Salary> </Employee> </EmployeeData> |
Cách tiếp cận này cho phép bạn sử dụng phương thức
getChildElements("Salary")
trên mỗi phần tử
Employee
để nhanh chóng tìm ra tổng số tiền
lương của nhân viên.
Tuy nhiên, việc sử dụng các tên cột Excel cho các tên phần tử là đầy nguy
hiểm. Các cột Excel có thể sử dụng các ký tự không hợp lệ trong các tên
phần tử XML, chẳng hạn như các khoảng trống. Vì vậy, hãy chắc chắn xóa các
ký tự đó ra khỏi các tên phần tử tiềm ẩn này.
Để cấu trúc dữ liệu theo cách này, bạn phải quen thuộc với dữ liệu. Thật
khó để tính toán theo lập trình rằng một hàng trong bảng tính Excel nên
được gọi là một
Employee
trong XML. Cũng sẽ rất
khó để tính toán một tên cho phần tử gốc (trong ví dụ trên là
EmployeeData
).
Ngoài ra, điều gì sẽ xảy ra nếu cấu trúc thay đổi hoặc Ông chủ Lớn muốn
phục hồi mã cho các bảng tính khác? Các hàng bảng tính có thể liệt kê các
loại chuột đồng chứ không phải là các nhân viên. Sau đó, bạn sẽ phải điều
chỉnh chương trình này gọi các hàng nào.
Xem xét việc pha trộn XML có cấu trúc-Excel với cách đánh dấu có cấu
trúc-dữ liệu, như trong Liệt kê 6.
Liệt kê 6. Pha trộn cấu trúc bảng tính với cách đánh dấu dựa trên-dữ liệu
<sheet> <row> <EmployeeLastName> Thumb </EmployeeLastName> <EmployeeFirstName> Green </EmployeeFirstName> <MainSuperPower> Growing Plants </MainSuperPower> <DailyStartTime> 5:00 AM </DailyStartTime> <DailyEndTime> 2:00 PM </DailyEndTime> <Salary> 150,000 </Salary> </row> </sheet> |
Nếu dòng đầu tiên trong mỗi bảng tính Excel chứa các tên cột, thì sự pha
trộn này có thể đủ linh hoạt để làm việc với một số bảng tính Excel. Vì dữ
liệu trong tài liệu và các hàng có thể không phù hợp giữa các bảng tính,
nên bạn có thể sử dụng
sheet
và
row
chung làm các tên phần tử, và chúng sẽ vẫn
có nghĩa với các nhà lập trình khi đọc mã.
Lưu ý: Theo cách đánh dấu trung tâm-dữ liệu thuần, hãy cẩn thận với
các ký tự trái luật chen vào các tên phần tử XML.
Bài viết này sử dụng một định dạng có pha trộn XML-Excel để lưu trữ các giá
trị của ô. Còn khả năng theo dõi thông tin ô khác thế nào, như kiểu và
định dạng dữ liệu?
Kiểu và định dạng dữ liệu là siêu dữ liệu. Tùy thuộc vào siêu dữ liệu nào
phải được bảo toàn, bạn có thể sử dụng các thuộc tính như
dataFormat
và
dataType
.
Mã hóa để chuyển đổi
Sau khi quyết định XML của bạn sẽ trông như thế nào, hãy bắt đầu lưu trữ dữ
liệu Excel trong các phần tử XML. Sử dụng các vòng lặp giống nhau để xem
xét kỹ các bảng tính Excel như trong Phần 1 của loạt bài này (xem phần Tài nguyên để biết liên kết). Sau đó, thêm XML.
Liệt kê 7 phục hồi mã đọc Excel từ bài viết
trước.
Liệt kê 7. Bắt đầu xem xét kỹ thông qua các bảng tính Excel (ExcelXML.java)
// Traverse the workbook's rows and cells. // Remember, excelWB is the workbook object obtained earlier. // Just use the first sheet in the book to keep the example simple. // Pretend this is an outer loop (looping through sheets). HSSFSheet oneSheet = excelWB.getSheetAt(0); // Now get the number of rows in the sheet int rows = oneSheet.getPhysicalNumberOfRows(); // Middle loop: Loop through rows in the sheet for (int rowNumber = 0; rowNumber < rows; rowNumber++) { HSSFRow oneRow = oneSheet.getRow(rowNumber); // Skip empty (null) rows. if (oneRow == null) { continue; } |
Khi bạn lặp qua hết các hàng của bảng tính, hãy tạo các phần tử XML để mô
tả các hàng, như trong Liệt kê 8.
Liệt kê 8. Tạo một phần tử hàng (ExcelXML.java)
// Create an XML element to represent the row. Element rowElement = new Element("row"); |
Bây giờ chưa đính kèm các hàng vào
Document
,
trong trường hợp bạn có hàng rỗng, hoặc hàng bằng không. Nếu hàng không
rỗng sau khi thêm các ô, bạn có thể gắn kèm nó với phần tử gốc ở dưới cùng
của vòng lặp hàng.
Tiếp theo, bắt đầu vòng lặp bên trong để đọc các ô, như trong Liệt kê 9.
Liệt kê 9. Tiếp tục lặp qua hết các ô Excel (ExcelXML.java)
// Get the number of cells in the row int cells = oneRow.getPhysicalNumberOfCells(); // Inner loop: Loop through each cell in the row for (int cellNumber = 0; cellNumber < cells; cellNumber++) { HSSFCell oneCell = oneRow.getCell(cellNumber); // If the cell is blank, the cell object is null, so don't // try to use it. It will cause errors. // Use continue to skip it and just keep going. if (oneCell == null) { continue; } |
Một khi bạn ở phía trong vòng lặp bên trong, hãy tạo một phần tử XML để mô
tả ô này, bằng cách sử dụng tên cột thích hợp làm tên phần tử. Trong Liệt kê 10, vì các tên phần tử không thể rỗng, nên
mỗi tên được mặc định theo
header
(tiêu đề).
Sau hàng đầu tiên, hãy tính toán một tên mới dựa vào dữ liệu được lưu trữ
trong các phần tử hàng đầu tiên, có chứa các tên cột bảng tính Excel. Liệt kê 10. Tạo các phần tử cho các ô bằng cách sử dụng các tên cột làm các tên phần tử (ExcelXML.java)
// Set up a string to use just "header" as the element name // to store the column header cells themselves. String elementName="header"; // Figure out the column position of the Excel cell. int cellColumnNumber = oneCell.getColumnIndex(); // If on the first Excel row, don't change the element name from "header," // because the first row is headers. Before changing the element name, // test to make sure you're past the first row, which is the zero row. if (rowNumber > 0) // Set the elementName variable equal to the column name elementName=reportRoot.getFirstChildElement("row").getChild(cellColumnNumber).getValue(); // Remove weird characters and spaces from elementName, // as they're not allowed in element names. elementName = elementName.replaceAll("[\\P{ASCII}]",""); elementName = elementName.replaceAll(" ", ""); // Create an XML element to represent the cell, using // the calculated elementName Element cellElement = new Element(elementName); |
Có nhiều thứ xảy ra trong Liệt kê 10. Trong dòng dài
ở giữa bên dưới nhận xét,
// Set the elementName variable equal to the column
name
,
elementName
được đặt bằng một cái gì đó. Khi
đọc đến cuối dòng này, bạn có thể thấy nó được đặt tới giá trị văn bản bên
trong một phần tử bằng cách sử dụng getValue()
.
Nó sử dụng giá trị của phần tử nào?
Bên trong
reportRoot
, bên trong phần tử hàng
first
(reportRoot.getFirstChildElement("row")
),
mã này xác định vị trí một phần tử con bằng số chỉ mục của nó khi sử dụng
getChild(cellColumnNumber)
. Bạn cũng đã lưu trữ
hàng đầu tiên của bảng tính này trong phần tử hàng đầu tiên, vì vậy các
giá trị của các phần tử con của hàng là các tên cột trong bảng tính. Số
chỉ mục giống như số cột của ô hiện tại trong bảng tính này. Do đó, giá
trị được thiết lập cho elementName
là tên cột
tương ứng từ hàng đầu tiên của các phần tử tiêu đề.
Tiếp theo, mã này xóa chuỗi
elementName
của các
ký tự trái luật có thể tồn tại trong các bảng tính. Đầu tiên, phương thức
replaceAll()
của
String
thay thế tất cả các ký tự không phải là
ASCII bằng các chuỗi rỗng. Sau đó, nó thay thế tất cả các khoảng trống. Cả
hai dòng đều sử dụng các biểu thức chính quy. Để biết thông tin về các
biểu thức chính quy, xem phần Tài nguyên.
Cuối cùng, dòng cuối cùng của Liệt kê 10 tạo phần tử
bằng cách sử dụng tên cột thích hợp.
Gắn thêm các thuộc tính và các phần
tử
Cũng giống như các phần tử, bạn có thể tạo các thuộc tính độc lập, và chúng
có thể vẫn còn trôi nổi tự do cho đến khi bạn gắn thêm chúng vào một phần
tử bằng cách sử dụng phương thức
addAttribute()
của nó. Tạo một thuộc tính, và sau đó đặt nó cùng với siêu dữ liệu ô bằng
cách sử dụng một phương thức getter như
getDataFormatString()
từ đối tượng
HSSFCell
của Apache POI, như trong Liệt kê 11. Liệt kê 11. Thêm các thuộc tính (ExcelXML.java)
// Create an attribute to hold the cell's format. // May be repeated for any other formatting item of interest. String attributeValue = oneCell.getCellStyle().getDataFormatString(); Attribute dataFormatAttribute = new Attribute("dataFormat", attributeValue); // Add the attribute to the cell element cellElement.addAttribute(dataFormatAttribute); |
Bây giờ, phần tử đã tồn tại và có một thuộc tính.
Để nhận được dữ liệu cho phần tử này, hãy nhớ kiểm tra kiểu dữ liệu của
từng
HSSFCell
, vì vậy bạn phải biết sử dụng
phương thức getter nào. Vì dù sao bạn cũng đang thử nghiệm kiểu dữ liệu,
nên bạn cũng có thể tạo một thuộc tính lưu trữ thông tin kiểu dữ liệu của
ô.
Việc thêm dữ liệu vào phần tử và gắn thêm phần tử làm phần tử con của hàng
là đơn giản khi làm việc với các giá trị chuỗi, như trong Liệt kê 12.
Liệt kê 12. Thêm các thuộc tính, gắn thêm văn bản ô vào phần tử, và gắn thêm phần tử vào trong hàng (ExcelXML.java)
switch (oneCell.getCellType()) { case HSSFCell.CELL_TYPE_STRING: // If the cell value is string, create an attribute // for the cellElement to state the data type is a string Attribute strTypeAttribute = new Attribute("dataType", "String"); cellElement.addAttribute(strTypeAttribute); // Append the cell text into the element cellElement.appendChild(oneCell.getStringCellValue()); // Append the cell element into the row rowElement.appendChild(cellElement); break; |
Lặp lại phần
case
cho mỗi kiểu dữ liệu. Tuy
nhiên, dữ liệu số có thể nan giải hơn. Phần 1 của loạt bài này đã chỉ ra
rằng dữ liệu Excel được trích xuất không giống như dữ liệu bảng tính (xem
liên kết đến Phần 1 trong phần Tài nguyên). Nó là
dữ liệu thô. Các số nào đó, như các ngày, trông rất lạ như dữ liệu thô và
sẽ không phải là các giá trị đúng nếu không được lưu trữ theo định dạng.
Bạn sẽ cần định dạng đúng dữ liệu số trước khi đặt cho một phần tử. Bạn có
thể sử dụng lớp HSSFDataFormatter
của Apache
POI và phương thức formatCellValue()
của nó để
thực hiện việc này. Xem Liệt kê 13. Liệt kê 13. Thêm các thuộc tính, định dạng dữ liệu số, và gắn thêm phần tử ô (ExcelXML.java)
case HSSFCell.CELL_TYPE_NUMERIC: // If the cell value is a number, create an attribute // for the cellElement to state the data type is numeric Attribute cellAttribute = new Attribute("dataType", "Numeric"); // Add the attribute to the cell cellElement.addAttribute(cellAttribute); // Apply the formatting from the cells to the raw data // to get the right format in the XML. First, create an // HSSFDataFormatter object. HSSFDataFormatter dataFormatter = new HSSFDataFormatter(); // Then use the HSSFDataFormatter to return a formatted string // from the cell rather than a raw numeric value: String cellFormatted = dataFormatter.formatCellValue(oneCell); //Append the formatted data into the element cellElement.appendChild(cellFormatted); // Append the cell element into the row rowElement.appendChild(cellElement); break; |
Lặp lại phần
case
cho mỗi kiểu ô có khả năng.
Xem toàn bộ ví dụ trong ExcelXML.java.
Sau khi lưu trữ dữ liệu ô, đóng vòng lặp bên trong. Trước khi kết thúc vòng
lặp giữa, dùng để mô tả các hàng, hãy kiểm tra xem phần tử
row
có rỗng không. Nếu không, gắn thêm nó vào
phần tử gốc. Sau đó, đóng vòng lặp giữa, như trong Liệt
kê 14. Liệt kê 14. Gắn thêm phần tử row vào phần tử gốc (ExcelXML.java)
// End inner loop } // Append the row element into the root // if the row isn't empty. if (rowElement.getChildCount() > 0) { reportRoot.appendChild(rowElement); } // End middle loop } |
Bây giờ tệp Excel của bạn là một tài liệu XML hoàn chỉnh.
Bên trong XML
Khi sử dụng XML để thực hiện các phép tính, chẳng hạn như tìm ra 1% tổng số
tiền lương, bạn phải chuyển đổi giữa các chuỗi và các số. Liệt kê 15 đưa ra một ví dụ.
Liệt kê 15. Tính 1% tiền lương và lưu trữ nó trong một phần tử đóng góp (ExcelXML.java)
// To get employees' salaries, iterate through row elements and get a collection of rows Elements rowElements = reportRoot.getChildElements("row"); // For each row element for (int i = 0; i < rowElements.size(); i++) { // Get the salary element, // Calculate 1% of it and store it in a donation element. // Unless it's the first row (0), which needs a header element. if (i==0) { Element donationElement = new Element("header"); donationElement.appendChild("Donation"); Attribute dataType = new Attribute("dataType","String"); donationElement.addAttribute(dataType); Attribute dataFormat = new Attribute("dataFormat","General"); donationElement.addAttribute(dataFormat); // Append the donation element to the row element. rowElements.get(i).appendChild(donationElement); } // If the row is not the first row, put the donation in the element. else { Element donationElement = new Element("Donation"); Attribute dataType = new Attribute("dataType","Numeric"); donationElement.addAttribute(dataType); // The dataFormat of the donation should be the same // number format as salary, which looking at the XML file tells // us is "#,##0". Attribute dataFormat = new Attribute("dataFormat","#,##0"); donationElement.addAttribute(dataFormat); // Get the salary element and its value Element salaryElement = rowElements.get(i).getFirstChildElement("Salary"); String salaryString = salaryElement.getValue(); // Calculate 1% of the salary. Salary is a string // with commas, so it // must be converted for the calculation. // Get a java.text.NumberFormat object for converting string to a double NumberFormat numberFormat = NumberFormat.getInstance(); // Use numberFormat.parse() to convert string to double. // Throws ParseException Number salaryNumber = numberFormat.parse(salaryString); // Use Number.doubleValue() method on salaryNumber to // return a double to use in the calculation. // Perform the calculation to figure out 1%. double donationAmount = salaryNumber.doubleValue()*.01; // Append the value of the donation into the donationElement. // donationAmount is a double and must be converted to a string. donationElement.appendChild(Double.toString(donationAmount)); // Append the donation element to the row element rowElements.get(i).appendChild(donationElement); //End else } // End for loop } |
Bây giờ bạn đã lưu trữ một phần tử
Donation
(đóng góp) bổ sung cho mỗi hàng. Bạn đã hoàn thành xây dựng đối tượng XML
Document của bạn.
XOM làm cho việc viết đối tượng
Document
của bạn
vào một tệp XML dễ dàng. Sử dụng
nu.xom.Serailizer.write()
, như trong Liệt kê 16. Liệt kê 16. Viết XML thành Excel (ExcelXML.java)
// Print out the XML version of the spreadsheet to see it in the console System.out.println(XMLReport.toXML()); // To save the XML into a file for GEE WHIS, start with a FileOutputStream // to represent the file to write, C:\Planet Power\GEE_WHIS.xml. FileOutputStream hamsterFile = new FileOutputStream("C:\\Planet Power\\GEE_WHIS.xml"); // Create a serializer to handle writing the XML Serializer saveTheHamsters = new Serializer(hamsterFile); // Set child element indent level to 5 spaces to make it pretty saveTheHamsters.setIndent(5); // Write the XML to the file C:\Planet Power\GEE_WHIS.xml saveTheHamsters.write(XMLReport); |
Các chuột đồng sẽ thích báo cáo đóng góp mới này của chúng. XML là ngôn ngữ
nguyên gốc của chúng.
Viết lại thành Excel
Để viết XML thành một bảng tính Excel, hãy lặp qua hết XML và thiết lập các
giá trị và định dạng ô. Liệt kê 17 thiết lập và bắt
đầu vòng lặp qua hết các hàng.
Liệt kê 17. Thiết lập để viết XML thành Excel (ExcelXML.java)
// Create a new Excel workbook and iterate through the XML // to fill the cells. // Create an Excel workbook object HSSFWorkbook donationWorkbook = new HSSFWorkbook(); // Next, create a sheet for the workbook. HSSFSheet donationSheet = donationWorkbook.createSheet(); // Iterate through the row elements and then cell elements // Outer loop: There was already an elements collection of all row elements // created earlier. It's called rowElements. // For each row element in rowElements: for (int j = 0; j < rowElements.size(); j++) { // Create a row in the workbook for each row element (j) HSSFRow createdRow = donationSheet.createRow(j); // Get the cell elements from that row element and add them to the workbook. Elements cellElements = rowElements.get(j).getChildElements(); |
Cũng giống như vòng lặp qua hàng, vòng lặp qua việc tạo ô là đơn giản. Phần
khó khăn hơn là định dạng các ô.
Một đối tượng
HSSFCellStyle
mô tả các tùy chọn
kiểu dáng cho một ô, như phông chữ, đường viền và định dạng số (bao gồm cả
định dạng ngày tháng và thời gian). Tuy nhiên, các kiểu dáng cho từng bảng
tính, không phải cho từng ô. Đối tượng
HSSFCellStyle
mô tả một kiểu dáng có tên hiện
có trong bảng tính, có thể được áp dụng cho một ô. Các kiểu dáng này là
các tập hợp nhóm của các tùy chọn kiểu dáng, giống như các kiểu dáng có
tên trong Microsoft Office Word. Cũng vậy, một
HSSFDataFormat
được tạo ra cho mỗi bảng tính,
nhưng chỉ mô tả định dạng số, như các định dạng ngày tháng và thời gian.
Để định kiểu một ô, hãy tạo một
HSSFCellStyle
mới cho bảng tính, hoặc sử dụng một
HSSFCellStyle
hiện có. Sau đó, đặt nó vào ô này
bằng cách sử dụng HSSFCell.setCellStyle()
. Để
thiết lập một định dạng số cho một ô, hãy thiết lập định dạng số của
HSSFCellStyle
, không phải của ô. Sau đó, áp
dụng HSSFCellStyle
cho ô này.
Các đối tượng
HSSFDataFormat
được lập chỉ mục
bằng số trong bảng tính. Để ra lệnh cho một
HSSFCellStyle
mà
HSSFDataFormat
cần sử dụng, bạn cần số chỉ mục
của HSSFDataFormat
. Đây là một số dạng ngắn,
không phải là một đối tượng HSSFDataFormat
.
May mắn thay, đối tượng
HSSFDataFormat
có một
phương thức có tên là getFormat()
. Khi đã
chuyển đi một chuỗi mô tả một định dạng mong muốn, nó trả về số chỉ mục
của HSSFDataFormat
khớp với chuỗi này. Chỉ mục
này được trả về là một số dạng ngắn. Nếu không khớp, nó tạo một
HSSFDataFormat
mới và trả về chỉ mục của nó.
Bạn có thể sử dụng chỉ mục đó để áp dụng định dạng cho kiểu dáng ô và áp
dụng kiểu dáng ô cho ô này, như trong Liệt kê 18. Liệt kê 18. Vòng lặp qua hết các phần tử ô và thiết lập định dạng số thích hợp trước khi chèn dữ liệu
// Middle loop: Loop through the cell elements. for (int k = 0; k < cellElements.size(); k++) { // Create cells and cell styles. Use the row's // createCell (int column) method. // The column index is the same as the cell element index, which is k. HSSFCell createdCell = createdRow.createCell(k); // To set the cell data format, retrieve it from the attribute // where it was stored: the dataFormat attribute. Store it in a string. String dataFormatString = cellElements.get(k).getAttributeValue("dataFormat"); // Create an HSSFCellStyle using the createCellStyle() method of the workbook. HSSFCellStyle currentCellStyle = donationWorkbook.createCellStyle(); // Create an HSSFDataFormat object from the workbook's method HSSFDataFormat currentDataFormat = donationWorkbook.createDataFormat(); // Get the index of the HSSFDataFormat to use. The index of the numeric format // matching the dataFormatString is returned by getFormat(dataFormatString). short dataFormatIndex = currentDataFormat.getFormat(dataFormatString); // Next, use the retrieved index to set the HSSFCellStyle object's DataFormat. currentCellStyle.setDataFormat(dataFormatIndex); // Then apply the HSSFCellStyle to the created cell. createdCell.setCellStyle(currentCellStyle); |
Sau khi thiết lập kiểu dáng của ô, hãy sử dụng
setCellValue()
để đặt hầu hết các kiểu dữ liệu
trong ô này
Tuy nhiên, dữ liệu số cần xử lý đặc biệt. Để lưu trữ các số như là các số
chứ không phải là văn bản, đầu tiên chuyển đổi các số lên số chính xác
kép. Không chuyển đổi các ngày thành chính xác kép, nếu không sẽ bị sai.
Kiểm tra định dạng dữ liệu của dữ liệu số để xác định xem đó có là một
ngày không (xem Liệt kê 19).
Liệt kê 19. Chèn dữ liệu ô, chuyển đổi lên gấp đôi cho dữ liệu số cụ thể
// Set cell value and types depending on the dataType attribute if (cellElements.get(k).getAttributeValue("dataType")=="String") { createdCell.setCellType(HSSFCell.CELL_TYPE_STRING); createdCell.setCellValue(cellElements.get(k).getValue()); } if (cellElements.get(k).getAttributeValue("dataType")=="Numeric") { createdCell.setCellType(HSSFCell.CELL_TYPE_NUMERIC); // In this spreadsheet, number styles are times, dates, // or salaries. To store as a number and not as text, // salaries should be converted to doubles first. // Dates and times should not be converted to doubles first, // or you'll be inputting the wrong date or time value. // Dates and times can be entered as Java Date objects. if (cellElements.get(k).getAttributeValue("dataFormat").contains("#")) { // If formatting contains a pound sign, it's not a date. // Use a Java NumberFormat to format the numeric type cell as a double, // because like before, the element has commas in it. NumberFormat numberFormat = NumberFormat.getInstance(); Number cellValueNumber = numberFormat.parse(cellElements.get(k).getValue()); createdCell.setCellValue(cellValueNumber.doubleValue()); // Add a hyperlink to the fictional GEE WHIS Web site just // to demonstrate that you can. HSSFHyperlink hyperlink = new HSSFHyperlink(HSSFHyperlink.LINK_URL); hyperlink.setAddress("http://www.ibm.com/developerworks/"); createdCell.setHyperlink(hyperlink); } else { // if it's a date, don't convert to double createdCell.setCellValue(cellElements.get(k).getValue()); } } // Handle formula and error type cells. See ExcelXML.java for the full example. //End middle (cell) for loop } // End outer (row) for loop } |
Định dạng cũng cần thiết cho các hàm Excel tạo các ngày, chẳng hạn như
TODAY()
và NOW()
.
Xem Liệt kê 20. Liệt kê 20. Sử dụng các hàm Excel với định dạng thích hợp
// Demonstrate functions: // Add the TODAY() and NOW() functions at bottom of the Excel report // to say when the workbook was opened. // Find the last row and increment by two to skip a row int lastRowIndex = donationSheet.getLastRowNum()+2; // Create a row and three cells to hold the information. HSSFRow lastRow = donationSheet.createRow(lastRowIndex); HSSFCell notationCell = lastRow.createCell(0); HSSFCell reportDateCell = lastRow.createCell(1); HSSFCell reportTimeCell = lastRow.createCell(2); // Set a regular string value in one cell notationCell.setCellValue("Time:"); // Setting formula values uses setCellFormula() reportDateCell.setCellFormula("TODAY()"); reportTimeCell.setCellFormula("NOW()"); // Create HSSFCellStyle objects for the date and time cells. // Use the createCellStyle() method of the workbook. HSSFCellStyle dateCellStyle = donationWorkbook.createCellStyle(); HSSFCellStyle timeCellStyle = donationWorkbook.createCellStyle(); // Get a HSSFDataFormat object to set the time and date formats for the cell styles HSSFDataFormat dataFormat = donationWorkbook.createDataFormat(); // Set the cell styles to the right format by using the index numbers of // the desired formats retrieved from the getFormat() function of the HSSFDataFormat. dateCellStyle.setDataFormat(dataFormat.getFormat("m/dd/yy")); timeCellStyle.setDataFormat(dataFormat.getFormat("h:mm AM/PM")); // Set the date and time cells to the appropriate HSSFCellStyles. reportDateCell.setCellStyle(dateCellStyle); reportTimeCell.setCellStyle(timeCellStyle); |
Cuối cùng, sau khi hoàn thành đối tượng bảng tính mong muốn, hãy ghi nó vào
một tệp bằng cách sử dụng phương thức
write()
của bảng tính (Liệt kê 21). Liệt kê 21. Viết bảng tính Excel vào một tệp
// Write out the workbook to a file. First, // you need some sort of OutputStream to represent the file. String filePathString = "C:\\Planet Power\\Employee_Donations.xls"; FileOutputStream donationStream = new FileOutputStream(filePathString); donationWorkbook.write(donationStream); |
Bây giờ bạn đã viết một bảng tính Excel tính toán các đóng góp cho WHIS
GEE.
Kết luận
Các báo cáo đã hoàn thành. Cùng với việc đọc Excel và tạo XML, bây giờ các
nhà lập trình Java có thể viết XML trở lại thành các tệp Excel. Sau khi đã
hiểu khái niệm cơ bản về chuyển đổi giữa hai định dạng, có lẽ bạn tự có
cảm giác nóng lên bao trùm toàn bộ ý tưởng viết báo cáo.
Ông chủ Lớn hạnh phúc, và bạn đã thực hiện một phần việc của mình với mục
đích giúp đỡ các siêu anh hùng môi trường của Điện lực Planet cứu các loài
chuột đồng hoang dã to lớn có sử dụng kỹ thuật di truyền học. Mọi người
đều hạnh phúc. Viết báo cáo thực sự có thể tốt cho môi trường.
Tải về
Mô tả | Tên | Kích thước | Phương thức tải |
---|---|---|---|
Sample Excel spreadsheet and Java code | Java-Excel-XML-Planet-Power2.zip | 17KB | HTTP |
Tài nguyên
Học tập
-
Đọc, phục hồi, sử dụng lại: Báo cáo được thực hiện dễ dàng với Excel,
XML, và các công nghệ Java, Phần 1: Đọc các tệp Excel và viết chúng
vào các tệp mới bằng cách sử dụng các công nghệ Java và XML
(Shaene M. Siders, developerWorks, 02.2010): Mỗi công ty phải đối mặt với
thách thức về trích xuất dữ liệu nghiệp vụ. Tìm hiểu để trích xuất dữ liệu
từ Excel và chuyển đổi nó giữa Excel và XML bằng cách sử dụng công nghệ
Java trong Phần 1 của loạt bài này của tác giả.
- Tài liệu javadoc cho
Apache POI: Duyệt tài liệu hướng dẫn Apache
POI.
- Các công nghệ XML và Java: Liên kết dữ liệu, Phần 2: Hiệu năng
(Dennis Sosnoski, developerWorks, 01.2003): Lấy các khung công tác liên
kết dữ liệu XML ra khỏi một ổ đĩa thử nghiệm.
- Các biểu
thức chính quy: Tìm hiểu thêm về các biểu thức chính quy bằng cách
sử dụng các biểu
thức chính quy trong Java và biểu
thức dấu ngoặc POSIX ASCII được sử dụng trong bài viết
này.
- Hướng dẫn
của các nhà phát triển bận rộn về các tính năng HSSF và XSSF: Bước
nhảy khởi động các kỹ năng POI của bạn với hướng dẫn này, có sẵn trên
trang web của Apache.
- Bắt đầu với XPath (Bertrand
Portier, developerWorks, 05.2004): Khám phá XPath và tìm hiểu về cú pháp
và các ngữ nghĩa, các đường dẫn vị trí XPath, các biểu thức XPath, các hàm
XPath của nó, và cách XPath liên quan đến XSLT trong hướng dẫn XPath mở
đầu này.
- XPath: Khám
phá XPath với W3Schools.
- Vùng XML trên developerWorks: Nhận được tài nguyên mà bạn cần để
nâng cao kỹ năng của bạn trong lĩnh vực XML.
-
Chứng chỉ XML
của IBM: Tìm hiểu cách bạn có thể trở thành một nhà phát triển có
chứng chỉ của IBM về XML và các công nghệ liên quan.
- Thư viện kỹ
thuật XML: Xem Vùng XML của developerWorks với một vùng rộng lớn các
bài viết kỹ thuật và các lời khuyên, các hướng dẫn, các tiêu chuẩn, và các
sách Đỏ của IBM.
- t của developerWorks: Theo sát
với công nghệ trong các phiên này.
- developerWorks trên Twitter: Tham gia ngày hôm nay để theo dõi
các tin tức của developerWorks.
- developerWorks podcasts: Nghe
các cuộc phỏng vấn và thảo luận thú vị dành cho các nhà phát triển phần
mềm.
Lấy sản phẩm và công nghệ
- Eclipse Classic: Tải về
Eclipse. Bài viết này sử dụng phiên bản 3.5.1.
- Apache POI: Tìm hiểu
thêm và tải về phiên bản 3.6 của Apache POI, là phiên bản ổn định mới
nhất.
- Hoàn thành nén tệp cho
XOM: Tìm hiểu thêm và tải về XML API của Elliotte Rusty
Harold.
- Kiểm tra một đoạn mã ngắn: Phát hiện (và loại bỏ) các hàng Excel
còn trống (nhưng khác null).
- Các phiên bản đánh giá sản phẩm IBM: Tải về hoặc khám phá các bản dùng thử trực tuyến trong Sandbox SOA của IBM và nhận được các công cụ phát triển ứng dụng thực hành của bạn và các sản phẩm phần mềm trung gian từ DB2®, Lotus®, Rational®, Tivoli®, và WebSphere®.
Nguồn IBM
0 comments:
Post a Comment