Hatena::ブログ(Diary)

Pastep RSSフィード Twitter

2011-12-29

JavaでXMLを読み込む

Java Advent Calendar 2011 29日目のエントリーです。


以前、XMLの読み込みにJAXB使った事があったんですが

JAXBのアンマーシャルでどろんこ遊び - Pastep

JAXBアンマーシャルでどろんこPart2 - Pastep

それ以外のXMLを取得する機能は使った事が無かったので

この機会に使ってみよう!という事で以下3つについて試して見ましたー。


Twitter検索で取得出来るatomから

ユーザー名と、ツイートを取得して表示するプログラムです。

SAX

public static void main(String[] args) throws Exception {

	SAXParserFactory factory = SAXParserFactory.newInstance();
	SAXParser parser = factory.newSAXParser();

	parser.parse("http://search.twitter.com/search.atom?q=インフラエンジニア双六",
		new DefaultHandler() {
			private String elementName = null;
			private StringBuilder name = new StringBuilder();
			private StringBuilder title = new StringBuilder();

			@Override
			public void startElement(String uri, String localName,
					String qName, Attributes attributes)
					throws SAXException {
				elementName = qName;
			}

			@Override
			public void characters(char[] ch, int start, int length)
					throws SAXException {
				if (elementName.equals("name")) {
					name.append(new String(ch, start, length));
				} else if (elementName.equals("title")) {
					title.append(new String(ch, start, length));
				}
			}

			@Override
			public void endElement(String uri, String localName,
					String qName) throws SAXException {
				if (qName.equals("entry")) {
					System.out.println(name.toString() + " : " + title.toString());
					name.setLength(0);
					title.setLength(0);
				}
			}
	});
}

DefaultHandlerを匿名クラスで拡張してパーサーに渡してます。

ハンドラの各メソッドを実装しておけば、XMLで該当の箇所を検出した時にメソッドを実行してくれると。

SAXってDOMと同じように標準規格だったんですね。

StAX

public static void main(String[] args) throws Exception {

	URL url = new URL("http://search.twitter.com/search.atom?q="
			+ URLEncoder.encode("インフラエンジニア双六", "UTF-8"));

	HttpURLConnection connection = (HttpURLConnection) url.openConnection();
	connection.connect();

	XMLInputFactory factory = XMLInputFactory.newInstance();

	XMLStreamReader reader = factory.createXMLStreamReader(connection.getInputStream());

	String elementName = null;
	StringBuilder name = new StringBuilder();
	StringBuilder title = new StringBuilder();

	while (reader.hasNext()) {

		switch (reader.next()) {
		case XMLStreamConstants.START_ELEMENT:
			elementName = reader.getLocalName();
			break;

		case XMLStreamConstants.CHARACTERS:
			if (elementName.equals("name")) {
				name.append(reader.getText());
			} else if (elementName.equals("title")) {
				title.append(reader.getText());
			}
			break;

		case XMLStreamConstants.END_ELEMENT:
			if (reader.getLocalName().equals("entry")) {
				System.out.println(name + " : " + title);
				name.setLength(0);
				title.setLength(0);
			}
			break;
		}
	}
	connection.disconnect();
}

Iteratorみたいな形でデータを取得出来るのですねー。

処理の主導権がSAXではパーサー側にあるのに対して、StAXでは実行側にある感じ。

EventIteratorAPIというcreateXMLStreamReaderではなく、

createXMLEventReaderで設定する形式もあるみたいですが。

SAXの各メソッド実装をイベントで登録出来るような物なのでしょうかねー…

また、今度試してみたいと思います。

DOM

public static void main(String[] args) throws Exception {
	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	DocumentBuilder builder = factory.newDocumentBuilder();

	Document document = builder.parse("http://search.twitter.com/search.atom?q=インフラエンジニア双六");

	Element rootElement = document.getDocumentElement();
	NodeList nodeList = rootElement.getElementsByTagName("entry");
	for (int i = 0; i < nodeList.getLength(); i++) {

		Element element = (Element) nodeList.item(i);

		String name = element.getElementsByTagName("name").item(0)
				.getFirstChild().getNodeValue();
		String title = element.getElementsByTagName("title").item(0)
				.getFirstChild().getNodeValue();

		System.out.println(name + " : " + title);
	}
}

JavaScriptでエレメントを取得しているのとほぼ同じ形ですねー。

速度とかメモリとか気にしないならこちらが一番わかりやすいかもです。