Memetakan XML ke Java, Bagian 1

XML itu keren. Karena XML adalah bentuk data yang mendeskripsikan diri sendiri, XML dapat digunakan untuk menyandikan model data yang kaya. Sangat mudah untuk melihat utilitas XML sebagai media pertukaran data antara sistem yang sangat berbeda. Data dapat dengan mudah diekspos atau dipublikasikan sebagai XML dari semua jenis sistem: program COBOL lama, database, program C ++, dan sebagainya.

KOLOM TULISAN:

TEXTBOX_HEAD: Memetakan XML ke Java: Baca keseluruhan seri!

  • Bagian 1 - Gunakan SAX API untuk memetakan dokumen XML ke objek Java
  • Bagian 2 - Buat perpustakaan kelas yang menggunakan SAX API untuk memetakan dokumen XML ke objek Java

: END_TEXTBOX

Namun, menggunakan XML untuk membangun sistem memiliki dua tantangan. Pertama, saat menghasilkan XML adalah prosedur langsung, operasi terbalik, menggunakan data XML dari dalam program, tidak. Kedua, teknologi XML saat ini mudah disalahgunakan, yang dapat membuat programmer menjadi sistem yang lambat dan haus memori. Memang, persyaratan memori yang berat dan kecepatan lambat dapat menjadi masalah bagi sistem yang menggunakan XML sebagai format pertukaran data utama mereka.

Beberapa alat standar yang saat ini tersedia untuk bekerja dengan XML lebih baik daripada yang lain. SAX API khususnya memiliki beberapa fitur runtime penting untuk kode yang sensitif terhadap kinerja. Pada artikel ini, kami akan mengembangkan beberapa pola untuk menerapkan SAX API. Anda akan dapat membuat kode pemetaan XML-ke-Java dengan cepat dengan jejak memori minimum, bahkan untuk struktur XML yang cukup kompleks (dengan pengecualian struktur rekursif).

Di Bagian 2 dari seri ini, kita akan membahas penerapan SAX API ke struktur XML rekursif di mana beberapa elemen XML mewakili daftar daftar. Kami juga akan mengembangkan perpustakaan kelas yang mengelola aspek navigasi SAX API. Library ini menyederhanakan penulisan kode pemetaan XML berdasarkan SAX.

Kode pemetaan mirip dengan kode kompilasi

Menulis program yang menggunakan data XML ibarat menulis compiler. Artinya, sebagian besar kompiler mengubah kode sumber menjadi program yang dapat dijalankan dalam tiga langkah. Pertama, modul lexer mengelompokkan karakter ke dalam kata-kata atau token yang dikenali oleh compiler - sebuah proses yang dikenal sebagai tokenisasi. Modul kedua, disebut parser, menganalisis kelompok token untuk mengenali konstruksi bahasa hukum. Terakhir, modul ketiga, generator kode, mengambil sekumpulan konstruksi bahasa hukum dan menghasilkan kode yang dapat dieksekusi. Terkadang, parsing dan pembuatan kode saling bercampur.

Untuk menggunakan data XML dalam program Java, kita harus menjalani proses serupa. Pertama, kami menganalisis setiap karakter dalam teks XML untuk mengenali token XML legal seperti tag awal, atribut, tag akhir, dan bagian CDATA.

Kedua, kami memverifikasi bahwa token membentuk konstruksi XML legal. Jika dokumen XML seluruhnya terdiri dari konstruksi hukum sesuai spesifikasi XML 1.0, dokumen itu dibuat dengan baik. Pada tingkat paling dasar, kita perlu memastikan bahwa, misalnya, semua penandaan memiliki tag pembuka dan penutup yang cocok, dan atribut terstruktur dengan benar di tag pembuka.

Selain itu, jika DTD tersedia, kami memiliki opsi untuk memastikan bahwa konstruksi XML yang ditemukan selama penguraian adalah legal dalam kaitannya dengan DTD, serta XML dengan format yang baik.

Akhirnya, kami menggunakan data yang terdapat dalam dokumen XML untuk mencapai sesuatu yang berguna - saya menyebutnya XML pemetaan ini ke dalam Java.

Pengurai XML

Untungnya, ada komponen off-the-shelf - XML ​​parser - yang melakukan beberapa tugas terkait compiler ini untuk kita. Pengurai XML menangani semua analisis leksikal dan tugas parsing untuk kita. Banyak parser XML berbasis Java yang saat ini tersedia mendukung dua standar parsing populer: SAX dan DOM API.

Ketersediaan parser XML off-the-shelf dapat membuatnya tampak bahwa bagian tersulit dalam menggunakan XML di Java telah dilakukan untuk Anda. Pada kenyataannya, menerapkan parser XML off-the-shelf adalah tugas yang terlibat.

SAX dan DOM API

SAX API berbasis peristiwa. Pengurai XML yang mengimplementasikan SAX API menghasilkan peristiwa yang sesuai dengan fitur berbeda yang ditemukan dalam dokumen XML yang diuraikan. Dengan menanggapi aliran peristiwa SAX ini dalam kode Java, Anda dapat menulis program yang didorong oleh data berbasis XML.

API DOM adalah API berbasis model objek. Pengurai XML yang mengimplementasikan DOM membuat model objek umum di memori yang mewakili konten dokumen XML. Setelah pengurai XML menyelesaikan penguraian, memori berisi pohon objek DOM yang menawarkan informasi tentang struktur dan konten dokumen XML.

Konsep DOM tumbuh dari dunia browser HTML, di mana model objek dokumen umum mewakili dokumen HTML yang dimuat di browser. DOM HTML ini kemudian tersedia untuk bahasa skrip seperti JavaScript. HTML DOM sangat sukses dalam aplikasi ini.

Bahaya DOM

Sekilas, DOM API tampaknya lebih kaya fitur, dan karena itu lebih baik, daripada SAX API. Namun, DOM memiliki masalah efisiensi serius yang dapat merusak aplikasi yang peka terhadap kinerja.

Grup pengurai XML saat ini yang mendukung DOM mengimplementasikan model objek dalam memori dengan membuat banyak objek kecil yang mewakili simpul DOM yang berisi teks atau simpul DOM lainnya. Ini terdengar cukup wajar, tetapi memiliki implikasi kinerja yang negatif. Salah satu operasi paling mahal di Jawa adalah newoperator. Sejalan dengan itu, untuk setiap newoperator yang dijalankan di Java, pengumpul sampah JVM pada akhirnya harus menghapus objek dari memori saat tidak ada referensi ke objek yang tersisa. API DOM cenderung benar-benar mengacaukan sistem memori JVM dengan banyak objek kecilnya, yang biasanya disingkirkan segera setelah penguraian.

Masalah DOM lainnya adalah fakta bahwa ia memuat seluruh dokumen XML ke dalam memori. Untuk dokumen berukuran besar, ini menjadi masalah. Sekali lagi, karena DOM diimplementasikan sebagai banyak objek kecil, jejak memori bahkan lebih besar daripada dokumen XML itu sendiri karena JVM menyimpan beberapa byte tambahan informasi mengenai semua objek ini, serta isi dari dokumen XML.

Ini juga mengganggu bahwa banyak program Java tidak benar-benar menggunakan struktur objek umum DOM. Sebaliknya, segera setelah struktur DOM dimuat dalam memori, mereka menyalin data ke dalam model objek khusus untuk domain masalah tertentu - proses yang halus namun boros.

Masalah halus lainnya untuk DOM API adalah bahwa kode yang ditulis untuknya harus memindai dokumen XML dua kali. Lintasan pertama membuat struktur DOM dalam memori, lintasan kedua menempatkan semua data XML yang diminati program. Gaya pengkodean tertentu mungkin melintasi struktur DOM beberapa kali tambahan saat menemukan potongan data XML yang berbeda. Sebaliknya, gaya pengkodean SAX mendorong pencarian dan pengumpulan data XML dalam sekali jalan.

Beberapa dari masalah ini dapat diatasi dengan desain struktur data dasar yang lebih baik untuk merepresentasikan model objek DOM secara internal. Masalah seperti mendorong beberapa proses berlalu dan menerjemahkan antara model objek generik dan spesifik tidak dapat diatasi dalam parser XML.

SAX untuk bertahan hidup

Compared to the DOM API, the SAX API is an attractive approach. SAX doesn't have a generic object model, so it doesn't have the memory or performance problems associated with abusing the new operator. And with SAX, there is no generic object model to ignore if you plan to use a specific problem-domain object model instead. Moreover, since SAX processes the XML document in a single pass, it requires much less processing time.

SAX does have a few drawbacks, but they are mostly related to the programmer, not the runtime performance of the API. Let's look at a few.

The first drawback is conceptual. Programmers are accustomed to navigating to get data; to find a file on a file server, you navigate by changing directories. Similarly, to get data from a database, you write an SQL query for the data you need. With SAX, this model is inverted. That is, you set up code that listens to the list of every available piece of XML data available. That code activates only when interesting XML data are being listed. At first, the SAX API seems odd, but after a while, thinking in this inverted way becomes second nature.

The second drawback is more dangerous. With SAX code, the naive "let's take a hack at it" approach will backfire fairly quickly, because the SAX parser exhaustively navigates the XML structure while simultaneously supplying the data stored in the XML document. Most people focus on the data-mapping aspect and neglect the navigational aspect. If you don't directly address the navigational aspect of SAX parsing, the code that keeps track of the location within the XML structure during SAX parsing will become spread out and have many subtle interactions. This problem is similar to those associated with overdependence on global variables. But if you learn to properly structure SAX code to keep it from becoming unwieldy, it is more straightforward than using the DOM API.

Basic SAX

There are currently two published versions of the SAX API. We'll use version 2 (see Resources) for our examples. Version 2 uses different class and method names than version 1, but the structure of the code is the same.

SAX is an API, not a parser, so this code is generic across XML parsers. To get the examples to run, you will need to access an XML parser that supports SAX v2. I use Apache's Xerces parser. (See Resources.) Review your parser's getting-started guide for specifics on invoking a SAX parser.

The SAX API specification is pretty straightforward. In includes many details, but its primary task is to create a class that implements the ContentHandler interface, a callback interface used by XML parsers to notify your program of SAX events as they are found in the XML document.

The SAX API also conveniently supplies a DefaultHandler implementation class for the ContentHandler interface.

Once you've implemented the ContentHandler or extended the DefaultHandler, you need only direct the XML parser to parse a particular document.

Our first example extends the DefaultHandler to print each SAX event to the console. This will give you a feel for what SAX events will be generated and in what order.

To get started, here's the sample XML document we will use in our first example:

   Bob   New York   

Next, we see the source code for XML mapping code of the first example:

import org.xml.sax.*; import org.xml.sax.helpers.*; import java.io.*; public class Example1 extends DefaultHandler { // Override methods of the DefaultHandler class // to gain notification of SAX Events. // // See org.xml.sax.ContentHandler for all available events. // public void startDocument( ) throws SAXException { System.out.println( "SAX Event: START DOCUMENT" ); } public void endDocument( ) throws SAXException { System.out.println( "SAX Event: END DOCUMENT" ); } public void startElement( String namespaceURI, String localName, String qName, Attributes attr ) throws SAXException { System.out.println( "SAX Event: START ELEMENT[ " + localName + " ]" ); // Also, let's print the attributes if // there are any... for ( int i = 0; i < attr.getLength(); i++ ){ System.out.println( " ATTRIBUTE: " + attr.getLocalName(i) + " VALUE: " + attr.getValue(i) ); } } public void endElement( String namespaceURI, String localName, String qName ) throws SAXException { System.out.println( "SAX Event: END ELEMENT[ " + localName + " ]" ); } public void characters( char[] ch, int start, int length ) throws SAXException { System.out.print( "SAX Event: CHARACTERS[ " ); try { OutputStreamWriter outw = new OutputStreamWriter(System.out); outw.write( ch, start,length ); outw.flush(); } catch (Exception e) { e.printStackTrace(); } System.out.println( " ]" ); } public static void main( String[] argv ){ System.out.println( "Example1 SAX Events:" ); try { // Create SAX 2 parser... XMLReader xr = XMLReaderFactory.createXMLReader(); // Set the ContentHandler... xr.setContentHandler( new Example1() ); // Parse the file... xr.parse( new InputSource( new FileReader( "Example1.xml" )) ); }catch ( Exception e ) { e.printStackTrace(); } } } 

Finally, here is the output generated by running the first example with our sample XML document:

Example1 SAX Events: SAX Event: START DOCUMENT SAX Event: START ELEMENT[ simple ] ATTRIBUTE: date VALUE: 7/7/2000 SAX Event: CHARACTERS[ ] SAX Event: START ELEMENT[ name ] SAX Event: CHARACTERS[ Bob ] SAX Event: END ELEMENT[ name ] SAX Event: CHARACTERS[ ] SAX Event: START ELEMENT[ location ] SAX Event: CHARACTERS[ New York ] SAX Event: END ELEMENT[ location ] SAX Event: CHARACTERS[ ] SAX Event: END ELEMENT[ simple ] SAX Event: END DOCUMENT 

As you can see, the SAX parser will call the appropriate ContentHandler method for every SAX event it discovers in the XML document.

Hello world

Sekarang setelah kita memahami pola dasar SAX, kita dapat mulai melakukan sesuatu yang sedikit berguna: mengekstrak nilai dari dokumen XML sederhana kita dan mendemonstrasikan program hello world klasik.