📄 jaxpxslt5.html
字号:
Running this program on <code class="cCode">PersonalAddressBook.ldif</code> produces this output:</p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">nickname: Fredemail: Fred@barneys.househtml: TRUEfirstname: Fredlastname: Flintstonework: 999-Quarryhome: 999-BedrockLanefax: 888-Squawkpager: 777-pagercell: 555-cell<a name="wp64748"> </a></pre></div><a name="wp64749"> </a><p class="pBody">I think we can all agree that's a bit more readable.</p><a name="wp64751"> </a><h3 class="pHeading2">Modifying the Parser to Generate SAX Events</h3><a name="wp64752"> </a><p class="pBody">The next step is to modify the parser to generate SAX events, so you can use it as the basis for a <code class="cCode">SAXSource</code> object in an XSLT <code class="cCode">transform</code>.</p><hr><a name="wp64753"> </a><p class="pNote">Note: The code discussed in this section is in <code class="cCode"><a href="../examples/jaxp/xslt/samples/AddressBookReader02.java" target="_blank">AddressBookReader02.java</a></code>. </p><hr><a name="wp64754"> </a><p class="pBody"> Start by importing the additional classes you're going to need:</p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">import java.io.*;<a name="wp64755"> </a><code class="cCodeBold">import org.xml.sax.*;import org.xml.sax.helpers.AttributesImpl;</code><a name="wp64756"> </a></pre></div><a name="wp64757"> </a><p class="pBody">Next, modify the application so that it extends <code class="cCode">XmlReader</code>. That change converts the application into a parser that generates the appropriate SAX events. </p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">public class AddressBookReader <code class="cCodeBold"> implements XMLReader</code>{ <a name="wp64758"> </a></pre></div><a name="wp64759"> </a><p class="pBody">Now, remove the <code class="cCode">main</code> method. You won't be needing that any more.</p><div class="pPreformattedRelative"><pre class="pPreformattedRelative"><code class="cCodeStruck">public static void main(String argv[]){ // Check the arguments if (argv.length != 1) { System.err.println ("Usage: Java AddressBookReader filename"); System.exit (1); } String filename = argv[0]; File f = new File(filename); AddressBookReader02 reader = new AddressBookReader02(); reader.parse(f);</code>}<a name="wp64760"> </a></pre></div><a name="wp66855"> </a><p class="pBody">Add some global variables that will come in handy in a few minutes: </p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">public class AddressBookReader implements XMLReader{ <code class="cCodeBold">ContentHandler handler; // We're not doing namespaces, and we have no // attributes on our elements. String nsu = ""; // NamespaceURI Attributes atts = new AttributesImpl(); String rootElement = "addressbook"; String indent = "\n "; // for readability!</code><a name="wp66861"> </a></pre></div><a name="wp64765"> </a><p class="pBody">The SAX <code class="cCode">ContentHandler</code> is the object that is going to get the SAX events the parser generates. To make the application into an <code class="cCode">XmlReader</code>, you'll be defining a <code class="cCode">setContentHandler</code> method. The <code class="cCode">handler</code> variable will hold a reference to the object that is sent when <code class="cCode">setContentHandler</code> is invoked.</p><a name="wp64766"> </a><p class="pBody">And, when the parser generates SAX <span style="font-style: italic">element</span> events, it will need to supply namespace and attribute information. Since this is a simple application, you're defining null values for both of those.</p><a name="wp64767"> </a><p class="pBody">You're also defining a root element for the data structure (<code class="cCode">addressbook</code>), and setting up an indent string to improve the readability of the output.</p><a name="wp64768"> </a><p class="pBody">Next, modify the <code class="cCode">parse</code> method so that it takes an <code class="cCode">InputSource</code> as an argument, rather than a <code class="cCode">File</code>, and account for the exceptions it can generate:</p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">public void parse(<code class="cCodeStruck">File f)</code><code class="cCodeBold">InputSource input)</code> <code class="cCodeBold">throws IOException, SAXException </code><a name="wp64769"> </a></pre></div><a name="wp64770"> </a><p class="pBody">Now make the changes shown below to get the reader encapsulated by the <code class="cCode">InputSource</code> object: </p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">try { // Get an efficient reader for the file <code class="cCodeStruck">FileReader r = new FileReader(f);</code> <code style="font-weight: bold" class="cCodeBold">java.io.Reader r = input.getCharacterStream()</code><span style="font-weight: bold">;</span> BufferedReader Br = new BufferedReader(r);<a name="wp64771"> </a></pre></div><hr><a name="wp64772"> </a><p class="pNote">Note: In the next section, you'll create the input source object and what you put in it will, in fact, be a buffered reader. But the <code class="cCode">AddressBookReader</code> could be used by someone else, somewhere down the line. This step makes sure that the processing will be efficient, regardless of the reader you are given.</p><hr><a name="wp64773"> </a><p class="pBody">The next step is to modify the <code class="cCode">parse</code> method to generate SAX events for the start of the document and the root element. Add the code highlighted below to do that:</p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">/** Parse the input */public void parse(InputSource input) ...{ try { ... // Read the file and display its contents. String line = br.readLine(); while (null != (line = br.readLine())) { if (line.startsWith("xmozillanickname: ")) break; }<code class="cCodeBold"> if (handler==null) { throw new SAXException("No content handler"); } handler.startDocument(); handler.startElement(nsu, rootElement, rootElement, atts);</code> output("nickname", "xmozillanickname", line); ... output("cell", "cellphone", line);<code class="cCodeBold"> handler.ignorableWhitespace("\n".toCharArray(), 0, // start index 1 // length ); handler.endElement(nsu, rootElement, rootElement);</code> <code class="cCodeBold"> handler.endDocument(); </code> } catch (Exception e) { ...<a name="wp64774"> </a></pre></div><a name="wp64775"> </a><p class="pBody">Here, you first checked to make sure that the parser was properly configured with a <code class="cCode">ContentHandler</code>. (For this app, we don't care about anything else.) You then generated the events for the start of the document and the root element, and finished by sending the end-event for the root element and the end-event for the document.</p><a name="wp64776"> </a><p class="pBody">A couple of items are noteworthy, at this point:</p><div class="pSmartList1"><ul class="pSmartList1"><a name="wp64777"> </a><div class="pSmartList1"><li>We haven't bothered to send the <code class="cCode">setDocumentLocator</code> event, since that is optional. Were it important, that event would be sent immediately before the <code class="cCode">startDocument</code> event. </li></div><a name="wp64778"> </a><div class="pSmartList1"><li>We've generated an <code class="cCode">ignorableWhitespace</code> event before the end of the root element. This, too, is optional, but it drastically improves the readability of the output, as you'll see in a few moments. (In this case, the whitespace consists of a single newline, which is sent the same way that characters are sent to the <code class="cCode">characters</code> method: as a character array, a starting index, and a length.)</li></div></ul></div><a name="wp64779"> </a><p class="pBody">Now that SAX events are being generated for the document and the root element, the next step is to modify the <code class="cCode">output</code> method to generate the appropriate element events for each data item. Make the changes shown below to do that:</p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">void output(String name, String prefix, String line) throws SAXException { int startIndex = prefix.length() + 2; // 2=length of ": " String text = line.substring(startIndex);<code class="cCodeStruck"> System.out.println(name + ": " + text);</code><code class="cCodeBold"> int textLength = line.length() - startIndex; handler.ignorableWhitespace(indent.toCharArray(), 0, // start index indent.length() ); handler.startElement(nsu, name, name /*"qName"*/, atts); handler.characters(line.toCharArray(), startIndex, textLength); handler.endElement(nsu, name, name);</code>}<a name="wp64780"> </a></pre></div><a name="wp64781"> </a><p class="pBody">Since the <code class="cCode">ContentHandler</code> methods can send <code class="cCode">SAXExceptions</code> back to the parser, the parser has to be prepared to deal with them. In this case, we don't expect any, so we'll simply allow the application to fail if any occur.</p><a name="wp64782"> </a><p class="pBody">You then calculate the length of the data, and once again generate some ignorable whitespace for readability. In this case, there is only one level of data, so we can use a fixed-indent string. (If the data were more structured, we would have to calculate how much space to indent, depending on the nesting of the data.)</p><hr><a name="wp64783"> </a><p class="pNote">Note: The indent string makes no difference to the data, but will make the output a lot easier to read. Once everything is working, try generating the result without that string! All of the elements will wind up concatenated end to end, like this:<br /><code class="cCode"><addressbook><nickname>Fred</nickname><email>...</code> </p><hr><a name="wp64784"> </a><p class="pBody">Next, add the method that configures the parser with the <code class="cCode">ContentHandler</code> that is to receive the events it generates: </p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">void output(String name, String prefix, String line) throws SAXException{ ...}/** Allow an application to register a content event handler. */<code class="cCodeBold">public void setContentHandler(ContentHandler handler) { this.handler = handler;}</code> <a name="wp66900"> </a>/** Return the current content handler. */<code class="cCodeBold">public ContentHandler getContentHandler() { return this.handler;}</code> <a name="wp64786"> </a></pre></div><a name="wp64787"> </a><p class="pBody">There are several more methods that must be implemented in order to satisfy the<code class="cCode"> XmlReader</code> interface. For the purpose of this exercise, we'll generate null methods for all of them. For a production application, though, you may want to consider implementing the error handler methods to produce a more robust app. For now, though, add the code highlighted below to generate null methods for them: </p><div class="pPreformattedRelative"><pre class="pPreformattedRelative">/** Allow an application to register an error event handler. */<code class="cCodeBold">public void setErrorHandler(ErrorHandler handler){ }</code><a name="wp64788"> </a>/** Return the current error handler. */<code class="cCodeBold">public ErrorHandler getErrorHandler()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -