Програмиране на XML в Java, част 1

И така, вие разбирате (повече или по-малко) как бихте представили данните си в XML и се интересувате от използването на XML за решаване на много от вашите проблеми с управлението на данни. И все пак не сте сигурни как да използвате XML с вашите Java програми.

TEXTBOX: TEXTBOX_HEAD: Програмиране на XML в Java: Прочетете цялата поредица!

  • Част 1. Използвайте простия API за XML (SAX), за да обработвате лесно XML в Java
  • Част 2. Научете за SAX и XML проверка чрез илюстративни примери
  • Част 3. ДОМИНАЦИЯ: Поемете контрола върху структурираните документи с обектния модел на документа

: END_TEXTBOX

Тази статия е продължение на моята уводна статия, "XML за абсолютно начинаещ", в броя на JavaWorld от април 1999 г. (вижте раздела Ресурси по-долу за URL адреса). Тази статия описва XML; Сега ще надграждам това описание и ще покажа подробно как да създам приложение, което използва Simple API за Java (SAX), лек и мощен стандартен Java API за обработка на XML.

Примерният код, използван тук, използва SAX API за четене на XML файл и създаване на полезна структура от обекти. Когато приключите с тази статия, ще сте готови да създадете свои собствени XML-базирани приложения.

Добродетелта на мързела

Лари Уол, луд гениален създател на Perl (вторият по големина език за програмиране в съществуването), заяви, че мързелът е една от „трите велики добродетели“ на програмист (другите две са нетърпение и пристрастие). Мързелът е добродетел, защото мързеливият програмист ще измине почти всякаква дължина, за да избегне работа, стигайки дори до създаване на общи, многократно използвани рамки за програмиране, които могат да се използват многократно. Създаването на такива рамки изисква много работа, но спестеното време за бъдещи задачи повече от компенсира първоначалните вложени усилия. Най-добрите рамки позволяват на програмистите да правят невероятни неща с малко или никаква работа - и затова мързелът е добродетелен.

XML е благоприятна технология за добродетелния (мързелив) програмист. Основният XML парсер прави много работа за програмиста, разпознавайки маркери, превеждайки кодирани символи, налагайки правила върху XML файловата структура, проверявайки валидността на някои стойности на данните и осъществявайки повиквания към специфичен за приложението код, където е подходящо. Всъщност ранната стандартизация, съчетана с ожесточено конкурентния пазар, доведе до множество свободно достъпни внедрения на стандартни XML парсери на много езици, включително C, C ++, Tcl, Perl, Python и, разбира се, Java.

API SAX е един от най-простите и леки интерфейси за работа с XML. В тази статия ще използвам внедряването на SAX на IBM XML4J, но тъй като API е стандартизиран, вашето приложение може да замести всеки пакет, който прилага SAX.

SAX е API, базиран на събития, работещ на принципа за обратно извикване. Програмистът на приложения обикновено създава SAX Parserобект и му предава едновременно входния XML и манипулатора на документи, който получава обратно извикване за SAX събития. SAX Parserпреобразува входа си в поток от събития, съответстващи на структурни характеристики на входа, като XML тагове или блокове текст. С настъпването на всяко събитие то се предава на подходящия метод на дефиниран от програмист манипулатор на документи, който реализира интерфейса за обратно извикване org.xml.sax.DocumentHandler. Методите в този клас манипулатори изпълняват специфичната за приложението функционалност по време на синтактичния анализ.

Например, представете си, че анализаторът на SAX получава документ, съдържащ малкия XML документ, показан в Листинг 1 по-долу. (Вижте Ресурси за XML файла.)

 Огден Неш Бълхи Адам  

Листинг 1. XML, представляващ кратко стихотворение

Когато анализаторът SAX срещне маркера, той извиква дефинирания от потребителя DocumentHandler.startElement()с низа POEMкато аргумент. Внедрявате startElement()метода, за да правите каквото и да е предназначено за приложението, когато POEMзапочва. Потокът от събития и получените повиквания за частта от XML по-горе се появява в Таблица 1 по-долу.

Таблица 1. Последователността на обратните обаждания, които SAX произвежда, докато анализира Листинг 1
Срещнат елемент Обратно обаждане на парсер
{Начало на документа} startDocument()
startElement("POEM", {AttributeList})
"\н" characters("\n...", 6, 1)
startElement("AUTHOR", {AttributeList})
"Огден Наш" characters("\n...", 15, 10)
endElement("AUTHOR")
"\н" characters("\n...", 34, 1)
startElement("TITLE", {AttributeList})
"Бълхи" characters("\n...", 42, 5)
endElement("TITLE")
"\н" characters("\n...", 55, 1)
startElement("LINE", {AttributeList})
"Адам" characters("\n...", 62, 4)
endElement("LINE")
startElement("LINE", {AttributeList})
- Имах ги. characters("\n...", 67, 8)
endElement("LINE")
"\н" characters("\n...", 82, 1)
endElement("POEM")
{Край на документа} endDocument()

Създавате клас, който се изпълнява, за DocumentHandlerда отговори на събития, които се случват в анализатора на SAX. Тези събития не са Java събития, тъй като може би ги знаете от набора от абстрактни прозорци (AWT). Те са условия, които анализаторът SAX открива при разбора му, като например началото на документ или появата на затварящ таг във входния поток. Тъй като всяко от тези условия (или събития) възниква, SAX извиква метода, съответстващ на условието в него DocumentHandler.

И така, ключът към писането на програми, които обработват XML със SAX, е да се разбере какво DocumentHandlerтрябва да се направи в отговор на поток от обратно извикване на метода от SAX. Анализаторът SAX се грижи за цялата механика на идентифициране на тагове, заместване на обектни стойности и така нататък, като ви оставя свободата да се концентрирате върху специфичната за приложението функционалност, която използва данните, кодирани в XML.

Таблица 1 показва само събития, свързани с елементи и знаци. SAX включва също съоръжения за обработка на други структурни характеристики на XML файлове, като обекти и инструкции за обработка, но те са извън обхвата на тази статия.

The astute reader will notice that an XML document can be represented as a tree of typed objects, and that the order of the stream of events presented to the DocumentHandler corresponds to an in-order, depth-first traversal of the document tree. (It isn't essential to understand this point, but the concept of an XML document as a tree data structure is useful in more sophisticated types of document processing, which will be covered in later articles in this series.)

The key to understanding how to use SAX is understanding the DocumentHandler interface, which I will discuss next.

Customize the parser with org.xml.sax.DocumentHandler

Since the DocumentHandler interface is so central to processing XML with SAX, it's worthwhile to understand what the methods in the interface do. I'll cover the essential methods in this section, and skip those that deal with more advanced topics. Remember, DocumentHandler is an interface, so the methods I'm describing are methods that you will implement to handle application-specific functionality whenever the corresponding event occurs.

Document initialization and cleanup

For each document parsed, the SAX XML parser calls the DocumentHandler interface methods startDocument() (called before processing begins) and endDocument() (called after processing is complete). You can use these methods to initialize your DocumentHandler to prepare it for receiving events and to clean up or produce output after parsing is complete. endDocument() is particularly interesting, since it's only called if an input document has been successfully parsed. If the Parser generates a fatal error, it simply aborts the event stream and stops parsing, and endDocument() is never called.

Processing tags

The SAX parser calls startElement() whenever it encounters an open tag, and endElement() whenever it encounters a close tag. These methods often contain the code that does the majority of the work while parsing an XML file. startElement()'s first argument is a string, which is the tag name of the element encountered. The second argument is an object of type AttributeList, an interface defined in package org.xml.sax that provides sequential or random access to element attributes by name. (You've undoubtedly seen attributes before in HTML; in the line

BORDER

Since SAX doesn't provide any information about the context of the elements it encounters (that appears inside in Listing 1 above, for example), it is up to you to supply that information. Application programmers often use stacks in startElement() and endElement(), pushing objects onto a stack when an element starts, and popping them off of the stack when the element ends.

Process blocks of text

The characters() method indicates character content in the XML document -- characters that don't appear inside an XML tag, in other words. This method's signature is a bit odd. The first argument is an array of bytes, the second is an index into that array indicating the first character of the range to be processed, and the third argument is the length of the character range.

It might seem that an easier API would have simply passed a String object containing the data, but characters() was defined in this way for efficiency reasons. The parser has no way of knowing whether or not you're going to use the characters, so as the parser parses its input buffer, it passes a reference to the buffer and the indices of the string it is viewing, trusting that you will construct your own String if you want one. It's a bit more work, but it lets you decide whether or not to incur the overhead of String construction for content pieces in an XML file.

The characters() method handles both regular text content and content inside CDATA sections, which are used to prevent blocks of literal text from being parsed by an XML parser.

Other methods

Има три други методи в DocumentHandlerинтерфейса: ignorableWhitespace(), processingInstruction(), и setDocumentLocator(). ignorableWhitespace()отчита появата на празно пространство и обикновено не се използва в невалидиращи SAX парсери (като този, който използваме за тази статия); processingInstruction()се справя с повечето неща отвътре and ?> delimiters; and setDocumentLocator() is optionally implemented by SAX parsers to give you access to the locations of SAX events in the original input stream. You can read up on these methods by following the links on the SAX interfaces in Resources.

Implementing all of the methods in an interface can be tedious if you're only interested in the behavior of one or two of them. The SAX package includes a class called HandlerBase that basically does nothing, but can help you take advantage of just one or two of these methods. Let's examine this class in more detail.

HandlerBase: A do-nothing class

Often, you're only interested in implementing one or two methods in an interface, and want the other methods to simply do nothing. The class org.xml.sax.HandlerBase simplifies the implementation of the DocumentHandler interface by implementing all of the interface's methods with do-nothing bodies. Then, instead of implementing DocumentHandler, you can subclass HandlerBase, and only override the methods that interest you.

For example, say you wanted to write a program that just printed the title of any XML-formatted poem (like TitleFinder in Listing 1). You could define a new DocumentHandler, like the one in Listing 2 below, that subclasses HandlerBase, and only overrides the methods you need. (See Resources for an HTML file of TitleFinder.)

012 /** 013 * SAX DocumentHandler class that prints the contents of "TITLE" element 014 * of an input document. 015 */ 016 public class TitleFinder extends HandlerBase { 017 boolean _isTitle = false; 018 public TitleFinder() { 019 super(); 020 } 021 /** 022 * Print any text found inside a  element. 023 */ 024 public void characters(char[] chars, int iStart, int iLen) { 025 if (_isTitle) { 026 String sTitle = new String(chars, iStart, iLen); 027 System.out.println("Title: " + sTitle); 028 } 029 } 030 /** 031 * Mark title element end. 032 */ 033 public void endElement(String element) { 034 if (element.equals("TITLE")) { 035 _isTitle = false; 036 } 037 } 038 /** 039 * Find contents of titles 040 */ 041 public static void main(String args[]) { 042 TitleFinder titleFinder = new TitleFinder(); 043 try { 044 Parser parser = ParserFactory.makeParser("com.ibm.xml.parsers.SAXParser"); 045 parser.setDocumentHandler(titleFinder); 046 parser.parse(new InputSource(args[0])); 047 } catch (Exception ex) { 048 ; // OK, so sometimes laziness *isn't* a virtue. 049 } 050 } 051 /** 052 * Mark title element start 053 */ 054 public void startElement(String element, AttributeList attrlist) { 055 if (element.equals("TITLE")) { 056 _isTitle = true; 057 } 058 } 

Listing 2. TitleFinder: A DocumentHandler derived from HandlerBase that prints TITLEs


#####


, е атрибут, чиято стойност е "1"). Тъй като Листинг 1 не включва атрибути, те не се показват в Таблица 1. Ще видите примери за атрибути в примерното приложение по-късно в тази статия.