Semplice parser SAX con Java
In questo breve articolo si mostra con un esempio come realizzare un parser XML SAX (Simple API for XML) in Java. Per il parsing sono utilizzate le librerie Xerces2 disponibili in ambiente Debian GNU/Linux con il pacchetto libxerces2-java.
Il programma realizzato come esempio è un semplice feed aggregator RSS: la sua funzionalità consiste nell’estrarre da una serie di fonti RSS, indicate tramite i loro URL, gli articoli che hanno nei loro titoli una parola chiave impostata come parametro dall’utente al lancio del programma.
Nel codice sono definite tre classi: RSSFeedAggregator rappresenta il feed aggregator e contiene i comandi per il parsing nonché il programma principale (metodo main); RSSItem descrive un articolo RSS nei soli suoi campi titolo, link e descrizione; RSSSAXHandler è l’handler che gestisce gli eventi durante la lettura del documento estraendo le informazioni desiderate.
Vista la semplicità del codice non mi dilungo in ulteriori spiegazioni, che eventualmente possono essere chieste via commenti o via email, ma lascio alla presentazione del codice del programma.
Di seguito il file sorgente (RSSFeedAggregator.java) del feed aggregator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | import java.io.IOException; import java.net.*; import java.util.*; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * Implementazione di un semplice feed aggregator di documenti RSS che estrae * da una serie di fonti solo gli articoli contenenti una determinata keyword * nel titolo (campo <code>title</code>). * * @author gilberto.taccari [AT] gmail.com * */ public class RSSFeedAggregator { private String keyword = null; private Vector urls = null; /** * Main program (chiamato da console). * @param args <code><parola chiave> <lista di feed></code> */ public static void main(String[] args) { RSSFeedAggregator rssFA = null; Vector items = null; // Articoli con keyword nel titolo Iterator it = null; if(args.length>1) { // Argomenti: almeno keyword e un URL rssFA = new RSSFeedAggregator(); rssFA.setKeyword(args[0]); // Aggiunge URL for(int i=1; i<args .length; i++) { try { rssFA.addRSSFeed(args[i]); } catch (MalformedURLException e) { System.out.println(args[i] + " URL non valido"); // e.printStackTrace(); } } // Parsing items = rssFA.parse(); // Stampa output it = items.iterator(); while(it.hasNext()) System.out.println(it.next().toString()); } else { // Il programma non viene chiamato in modo corretto System.out.println( "Usage: java RSSFeedAggregator " ); } } /** * Crea un feed aggregator RSS */ public RSSFeedAggregator() { this.urls = new Vector(); } /** * Effettua il parsing dei titoli dei feed restituendo solo gli articoli * contenenti la parola chiave * @return vettore con gli articoli estratti dai feed */ public Vector parse() { Vector items = new Vector(); Iterator it = null; // javax.xml.parsers.SAXParserFactory SAXParserFactory spf = SAXParserFactory.newInstance(); try { SAXParser sp = spf.newSAXParser(); // Parser /* * Per ogni URL viene chiamato il metodo che effettua il parsing * degli articoli contenuti nel documento XML */ it = this.urls.iterator(); while(it.hasNext()) { sp.parse( ((URL)it.next()).toExternalForm(), // Stringa URL new RSSSAXHandler(this.keyword, items) ); } } catch (ParserConfigurationException e) { // TODO Auto-generated catch block // e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block // e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block // e.printStackTrace(); } return items; } /** * Imposta la parola da cercare nei titoli degli articoli dei feed * @param keyword parola chiave con cui vengono scelti gli articoli */ public void setKeyword(String keyword) { this.keyword = keyword; } /** * Aggiunge l'URL di un feed alla lista di quelli da analizzare * @param feedURL stringa con URL del feed RSS * @throws MalformedURLException se l'URL non è nel formato corretto */ public void addRSSFeed(String feedURL) throws MalformedURLException { URL urlObj = new URL(feedURL); this.urls.add(urlObj); } } /** * Rappresenta un articolo prelevato dal feed RSS. Considera solo gli elementi * <code>title</code>, <code>link</code>, <code>description</code>. * @author Gilberto Taccari * */ class RSSItem { // Dati relativi a un item private String title = ""; private String link = ""; private String description = ""; public RSSItem() { // Nothing } public void setTitle(String text) { this.title = text; } public String getTitle() { return this.title; // title: unico campo per cui serve il metodo get } public void setLink(String text) { this.link = text; } public void setDescription(String text) { this.description = text; } public String toString() { return "* " + title + "\n" + " " + link + "\n" + description + "\n"; } } /** * Handler che effettua il parsing e costruisce una lista di RSSItem * @author Gilberto Taccari * */ class RSSSAXHandler extends DefaultHandler { private String keyword = null; private Vector items = null; /** * Stringa contenente il testo tra due tag di interesse */ private String tempVal = null; /** * Item */ private RSSItem tempItem = null; /** * Handler per il parsing dei feed * @param keyword parola chiave con cui vengono "filtrati" gli articoli * @param items vettore dove vengono memorizzati gli articoli */ public RSSSAXHandler(String keyword, Vector items) { super(); this.keyword = keyword; this.items = items; } /** * Riceve la notifica dei caratteri all'interno di un elemento */ public void characters(char[] ch, int start, int length) throws SAXException { tempVal = new String(ch, start, length); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { // System.out.println(qName); /* * Impedisce che venga eseguito il metodo quando viene incontrato un * elemento che termina senza una precedente apertura */ if(tempItem==null) return; // Item if(qName.equalsIgnoreCase("item")) { // Confronto con entrambe le stringhe in lowercase String lTitle = (tempItem.getTitle()).toLowerCase(); String lKeyword = this.keyword.toLowerCase(); if((lTitle.indexOf(lKeyword))!=-1) { items.add(tempItem); // Aggiunge l'articolo all'elenco // System.out.println("TROVATO " + tempItem.getTitle()); } } // Title if(qName.equalsIgnoreCase("title")) tempItem.setTitle(tempVal); // Link if(qName.equalsIgnoreCase("link")) tempItem.setLink(tempVal); // Description if(qName.equalsIgnoreCase("description")) tempItem.setDescription(tempVal); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { tempVal = ""; // Reset if(qName.equalsIgnoreCase("item")) { tempItem = new RSSItem(); } } } |