stax使ってみますよ〜
staxでxmlをパースするサンプルです。
XMLStreamReaderとXMLEventReaderがありますが、XMLEventReaderは微妙に使いにくいのでXMLStreamReaderを使います。
livedoor天気情報のapiを使って、locationの属性名・値、descriptionの要素名・値を取得します。
apiは以下のようなxmlを返します。
<lwws version="livedoor Weather Web Service 1.0"> <author>livedoor Weather Team.</author> <location area="関東" pref="神奈川県" city="横浜"/> <title>神奈川県 横浜 - 明日の天気</title> <link>http://weather.livedoor.com/area/14/70.html?v=1</link> <forecastday>tomorrow</forecastday> <day>Thursday</day> <forecastdate>Thu, 14 Jun 2012 00:00:00 +0900</forecastdate> <publictime>Tue, 12 Jun 2012 17:00:00 +0900</publictime> <telop>曇時々晴</telop> <description> 神奈川県では、12日夜のはじめ頃から13日昼前まで強風に注意して下さい。 本州の南海上には前線が停滞しています。前線上の四国沖には低気圧があって東に進んで...<br />[PR]<a href="http://weather.livedoor.com/indexes/cloth_wash/?r=rest_pr">きょうは洗濯できるかな?</a> </description> ・・・以下略・・・
ではこのxmlレスポンスをパースするコードを書いてみます。
public class XMLStreamReaderTest { public static void main(String[] args) throws MalformedURLException, IOException, XMLStreamException { URLConnection con = new URL("http://weather.livedoor.com/forecast/webservice/rest/v1?city=70&day=tomorrow").openConnection(); Reader r = new InputStreamReader(con.getInputStream(), "UTF-8"); new XMLStreamReaderTest().run(r); } public void run(Reader r) throws FileNotFoundException, XMLStreamException { final StreamFilter filter = new StreamFilter() { @Override public boolean accept(XMLStreamReader reader) { return reader.isStartElement() || reader.isEndElement(); } }; XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createFilteredReader(factory.createXMLStreamReader(r), filter); while (reader.hasNext()) { reader.next(); if (reader.isStartElement()) { System.out.println("START:" + reader.getLocalName()); if ("location".equals(reader.getLocalName())) { for (int i = 0; i < reader.getAttributeCount(); i++) { System.out.println(reader.getAttributeLocalName(i) + "=" + reader.getAttributeValue(i)); } } else if ("description".equals(reader.getLocalName())) { System.out.println(reader.getElementText()); } } else if (reader.isEndElement()) { System.out.println("END:" + reader.getLocalName()); } } reader.close(); } }
実行すると以下のようにコンソールに表示されます。
START:author END:author START:location area=関東 pref=神奈川県 city=横浜 END:location ・・・略・・・ START:description 神奈川県では、12日夜のはじめ頃から13日昼前まで強風に注意して下さい。 本州の南海上には前線が停滞しています。前線上の四国沖には低気圧があって東に進んで...<br />[PR]<a href="http://weather.livedoor.com/indexes/cloth_wash/?r=rest_pr">きょうは洗濯できるかな?</a> ・・・略・・・
実際パースする場合は結果をdtoのlistに詰めて永続化する事が多いとおもいます。
例えば以下のようなxmlが返る場合、
<pinpoint> <location> <title>横浜市</title> </location> <location> <title>横浜市鶴見区</title> </location> </pinpoint>
isStartElement()でdtoをnewし、isEndElement()でlistにdtoをaddすれば簡単にlistに詰められるかと思います。
List<HogeDto> list = new ArrayList<HogeDto>(); HogeDto dto = null; while (reader.hasNext()) { reader.next(); if (reader.isStartElement()) { if ("location".equals(reader.getLocalName())) { dto = new HogeDto(); } else if ("title".equals(reader.getLocalName())) { dto.title = reader.getElementText(); } } else if (reader.isEndElement()) { if ("location".equals(reader.getLocalName())) { list.add(dto); } } }
コードを見ただけではxmlの構造が想像できませんが、
domより綺麗で良い感じのコードになりますね。