Đọc, phục hồi và sử dụng lại: Báo cáo được tạo dễ dàng với Excel, XML, và các công nghệ Java, Phần 2

Chuyển đổi giữa các định dạng báo cáo XML và Excel

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.

Shaene M Siders, Tác giả và nhà tư vấn, Dragon Under Glass

Shaene Siders là một nhà tư vấn và người viết về công nghệ, đào tạo và giải trí. Cô đã thành lập một công ty phát triển web và viết sách vào năm 1996 và đã là Trợ giáo có chứng chỉ Rational của IBM về ClearCase và ClearQuest (IBM Rational Certified Instructor ClearCase và ClearQuest) kể từ năm 2003. Cô là tác giả và nhà sản xuất loạt phần mềm tin học dành cho đào tạo Java for Mad Scientists, (Java cho các nhà khoa học say mê) và cô cũng là một Trợ giáo có chứng chỉ Rational của IBM về Build Forge, Rational Functional Tester, Rational Performance Tester và Rational Manual Tester. Bạn có thể liên lạc với Shaene tại consult@DragonUnderGlass.com.



29 11 2011

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.

Các từ viết tắt thông dụng

  • API: Giao diện lập trình ứng dụng
  • ASCII: Bộ mã chuẩn để trao đổi thông tin của Mỹ
  • HSSF: Định dạng Bảng tính đáng ghét
  • HTML: Ngôn ngữ đánh dấu siêu văn bản
  • XML: Ngôn ngữ đánh dấu mở rộng
  • XSSF: Định dạng bảng tính XML

Ứ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:

  1. 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).
  2. 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 chụp màn hình của hộp thoại Eclipse Import với một dự án hiện có được chọn để đặt vào trong vùng làm việc
  3. 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.
  4. 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
    Ảnh chụp màn hình của hộp thoại Eclipse Import với Kết thúc nhập dự án vào trong 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().


Đọc các tệp XML

Đọc các tệp XML

Bài viết này trình bày việc viết XML nhưng không đọc các tệp XML. Để có thông tin về việc đọc XML, hãy xem liên kết đến trang Web XOM đã cung cấp trong phần Tài nguyên hoặc mẫu mã XMLReader.java trong dự án Eclipse được tải về.

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.

Phương thức đánh dấu 3: Pha trộn ứng dụng hỗn hợp Excel XML

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 sheetrow 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ư dataFormatdataType.


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 HSSFCellStyleHSSFDataFormat 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()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ênKích thước
Sample Excel spreadsheet and Java codeJava-Excel-XML-Planet-Power2.zip17KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở, Công nghệ Java
ArticleID=777150
ArticleTitle=Đọc, phục hồi và sử dụng lại: Báo cáo được tạo dễ dàng với Excel, XML, và các công nghệ Java, Phần 2
publish-date=11292011