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>&lt;parola chiave&gt; &lt;lista di feed&gt;</code>
	 */
	public static void main(String[] args) {
		RSSFeedAggregator rssFA = null;
		Vector items = null; // Articoli con keyword nel titolo
		Iterator it = null;
 
		if(args.length&gt;1) { // Argomenti: almeno keyword e un URL
			rssFA = new RSSFeedAggregator();
			rssFA.setKeyword(args[0]);
 
			// Aggiunge URL
			for(int i=1; i&lt;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 &egrave; 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();
		}
	}
}

Tags: , , , , , ,

Leave a Reply

Per evitare la molesta presenza di commenti inviati automaticamente da programmi software e contenenti messaggi pubblicitari o di altro genere, il sito fa uso del CAPTCHA. Ricopiare il codice visualizzato nell'immagine a lato sul seguente campo di input.

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>