XMLパーサー


通常は、XMLファイルはjavax.xml を使って解析します。そしてそのバックエンドはApache Xalanになると思います。
あとXML Transformとかも流行っているみたいが、ここでは紹介できません。

ここでは、他のパーサーを主に紹介したいと思います。

例として、こういうRSSファイルをCSVに変換してみます。
真面目にRSSをJavaで扱う場合は、このリンク先 からJava RSSパーサー見つけて使うのいいでしょう。また出力ならVelocityなどテンプレートエンジンの方が楽な場合もあります。
<rss version="2.0">
  <channel>
<title>AkiのWebサイト</title>
    <link>http://www.xucker.jpn.org</link>
 <item>
      <title>Web自動テストツール Canoo WebTestについて</title>
      <link>http://java.akjava.com/library/canoowebtest</link>
      <pubDate>Fri, 05 Feb 2010 19:36:42 +0000</pubDate>
    </item>
</channel>
</rss>


EclipseのJetty XmlParser

あまり使われている例はありませんが、シンプルに書けるので私は好きです。
nodeがiteratorになっているのと、getで手軽にNodeが取れるのが楽です。

Iterator<XmlParser.Node> iter=node.get("channel").iterator("item");
String title=item.get("title").get(0).toString().trim();

だいたいjetty-xml.jarとjetty-util.jar で実行可能です。


import org.eclipse.jetty.xml.XmlParser;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.xml.sax.SAXException;


public class Jettyxml {

/**
* @param args
*/
public static void main(String[] args) {
XmlParser parser=new XmlParser();
try {
Map<String,String> map=new LinkedHashMap<String, String>();
XmlParser.Node node=parser.parse("c:\\tmp\\test.xml");
Iterator<XmlParser.Node> iter=node.get("channel").iterator("item");
while(iter.hasNext()){
XmlParser.Node item=iter.next();
String title=item.get("title").get(0).toString().trim();
String url=item.get("link").get(0).toString().trim();
map.put(title, url);
}
 //print linked hashlist
  for(String title:map.keySet()){
     System.out.println(title+","+map.get(title));
     }
  
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}

}

HTMLUnitのXMLPage


HTMLUnitでもXMLを処理可能です。HTMLUnitだともしかしたら、invalidなXMLもうまく解析出来るかもしれません。
ただし、ローカルのファイルの扱い方はよくわかりませんでした。(UnknownPageになります)


普通に取得するとHTMLPageに変換されてしまうので、
 
setWebConnectionを使ってXMLとして認識させます。

final WebClient webClient = new WebClient();
try {
webClient.setWebConnection(new FalsifyingWebConnection(webClient) {
                @Override
                public WebResponse getResponse(WebRequestSettings settings) throws IOException {
                    WebResponse response = (WebResponse) super.getResponse(settings);
                    response = createWebResponse(settings, response.getContentAsString(), "text/xml");
                    return response;
                }
            });
final XmlPage page = webClient.getPage("http://localhost/rss.xml");

基本的に普通のDomの扱いなのですが、getElementBy* が未実装なため
 
getByXPath でNodeを探すことになります。

List <Node> nodes=(List<Node>) page.getByXPath("//item");


HtmlUnitParse.java

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.util.FalsifyingWebConnection;
import com.gargoylesoftware.htmlunit.xml.XmlPage;


public class HtmlUnitParse {

/**
* @param args
*/
public static void main(String[] args) {
Map<String,String> map=new LinkedHashMap<String, String>();
final WebClient webClient = new WebClient();
try {
webClient.setWebConnection(new FalsifyingWebConnection(webClient) {
                @Override
                public WebResponse getResponse(WebRequestSettings settings) throws IOException {
                    WebResponse response = (WebResponse) super.getResponse(settings);
                    response = createWebResponse(settings, response.getContentAsString(), "text/xml");
                    return response;
                }
            });
final XmlPage page = webClient.getPage("http://localhost/rss.xml");

List <Node> nodes=(List<Node>) page.getByXPath("//item");
for(int i=0;i<nodes.size();i++){
Node n=nodes.get(i);
NodeList nlist=n.getChildNodes();
String title=null;
String url=null;
for(int j=0;j<nlist.getLength();j++){
Node child=nlist.item(j);
if(child.getNodeName().equals("title")){
title=child.getTextContent().trim();
}else if(child.getNodeName().equals("link")){
url=child.getTextContent().trim();
}
if(title!=null && url!=null){
map.put(title, url);
}
}
}
} catch (FailingHttpStatusCodeException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//print linked hashlist
  for(String title:map.keySet()){
     System.out.println(title+","+map.get(title));
     }
}

}



Saxを使う方法

古いやり方ですが、Saxを使う方法もあります。Saxを使うとかなり複雑な構造でも処理出来ます。
Saxのメリットとしてメモリの消費量が少ないという点がありますので、Androidとかのモバイルでは主流かもしれません。

SaxTest.java

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;


public class SaxTest extends DefaultHandler2{

/**
* @param args
*/
public static void main(String[] args) {
try {
   
     SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
     saxParserFactory.setValidating(true);
    
   
     BufferedInputStream input = new BufferedInputStream(new FileInputStream(new File("c:\\tmp\\test.xml")));
    
     SAXParser saxParser = saxParserFactory.newSAXParser();
     InputSource inputSource = new InputSource(input);
   
     
     SaxTest test=new SaxTest();
     saxParser.parse(inputSource, test);
     for(String title:test.map.keySet()){
     System.out.println(title+","+test.map.get(title));
     }
   } catch (Exception e) {
     System.err.println(e);
   }
}
private Map<String,String> map=new LinkedHashMap<String, String>();

private String title;
private String url;
private boolean isTitle;
private boolean isUrl;

public void characters(char[] ch, int start, int length) {
String text= new String(ch, start, length);
text=text.trim();
if(!text.isEmpty()){
if(isTitle){
title=text;
}else if(isUrl){
url=text;
}
}
 }
public void startElement(String namespaceURI,
            String localName,
            String qName,
            Attributes atts) 
    throws SAXException
{
if(qName.equals("title")){
isTitle=true;
}else if(qName.equals("link")){
isUrl=true;
}else if(qName.equals("item")){
}
}
 public void endElement(String namespaceURI,
                        String localName,
                        String qName) {
 
 if(qName.equals("title")){
isTitle=false;
}else if(qName.equals("link")){
isUrl=false;
}else if(qName.equals("item")){
map.put(title, url);
title=null;
url=null;
}
 }
 
}


Domを使って解析


XMLを解析するうえで、一番、単純な方法だと思います。
Domを使って解析はほとんどどの環境でも使えるので最低限は使えるようになっていると便利ですよ。
NodeList list=dc.getElementsByTagName("item");
for(int i=0;i<list.getLength();i++){
                    ...

ParseXml

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


public class ParseXml {

/**
* @param args
*/
public static void main(String[] args) {
Map<String,String> map=new LinkedHashMap<String, String>();
try {
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder bulder=factory.newDocumentBuilder();
InputStream input=new URL("file:///C:/tmp/test.xml").openStream();
Document dc=bulder.parse(input);
NodeList list=dc.getElementsByTagName("item");
for(int i=0;i<list.getLength();i++){
Node n=list.item(i);
NodeList nlist=n.getChildNodes();
String title=null;
String url=null;
for(int j=0;j<nlist.getLength();j++){
Node child=nlist.item(j);
if(child.getNodeName().equals("title")){
title=child.getTextContent().trim();
}else if(child.getNodeName().equals("link")){
url=child.getTextContent().trim();
}
if(title!=null && url!=null){
map.put(title, url);
}
}
}

} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//print linked hashlist
  for(String title:map.keySet()){
     System.out.println(title+","+map.get(title));
     }
}

}



Comments