用java sax处理xml文件(DBLP数据集)

标签: java sax xml | 发表时间:2016-07-26 16:57 | 作者:u013468917
出处:http://blog.csdn.net

在java中,可以用多种方式处理xml文件。前一段时间因为要使用到dblp数据集,而且这个数据集比较大无法一次性加载到内存中解析成文档树再处理。所以只能用sax的方式边读边处理。

下面是dblp数据集的简介,在处理xml文件之前,对xml的结构的了解很重要:

DBLP是计算机领域内对研究的成果以作者为核心的一个计算机类英文文献的集成数据库系统,按年代列出了作者的科研成果。包括国际期刊和会议等公开发表的论文。DBLP没有提供对中文文献的收录和检索功能,国内类似的权威期刊及重要会议论文集成检索系统有C-DBLP。

这个项目是德国特里尔大学的Michael Ley负责开发和维护。它提供计算机领域科学文献的搜索服务,但只储存这些文献的相关元数据,如标题,作者,发表日期等。和一般流行的情况不同,DBLP并没有使用数据库而是使用XML存储元数据。

DBLP在学术界声誉很高,而且很多论文及实验都是基于DBLP的。所收录的期刊和会议论文质量较高,也比较全面。文献更新速度很快,能很好地反应了国外学术研究的前沿方向。

在下载DBLP数据集后要记得下载dblp.dtd文件,这个文件是对数据集结构的说明:

数据集的根节点是: dblp 二级节点有:article|inproceedings|proceedings|book|incollection|phdthesis|mastersthesis|www 这些节点标识了文献的类型。三级节点有:booktitle ,pages,year。。。。等等,分别标识文献的题目/页数/年份等属性。

我的解析目标是将这个xml文件的数据集解析出来存储到关系型数据库中,比如mysql。


下面简单介绍一下如何再java用sax的方式解析xml文档:

主要需要写一个类继承 org.xml.sax.helpers.DefaultHandler(注意这里并非实现接口)

重写如下方法,这里对方法的作用也进行了说明:

/*用来遍历xml的开始标签
  参数说明:
  uri:标签的命名空间uri,如果标签没有命名空间或者不需要命名空间处理,则为空值
  localName:本地名,不需要命名空间处理则为空
  qName:当前遍历的标签名
  attributes:标签的属性,可以为空
*/
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		// TODO Auto-generated method stub

	}
	
	//用来遍历xml的结束标签
	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		// TODO Auto-generated method stub
		
	}
	/*
	获取标签中间的字符数据。比如对于节点<year>1991</year>获取的即为1991
	ch:字符数据(以字符数组的形式)
	start:字符数组中的起始位置
	length:从起始位置开始要取用的字符个数
	可以通过String的构造方法获取自己需要的字符串
	*/
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		// TODO Auto-generated method stub
		
	}
	
	//用来标识解析开始
	@Override
	public void startDocument() throws SAXException {
		// TODO Auto-generated method stub
		
	}
	
	//用来标识解析结束
	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		
	}

基于上述框架,我的解析思路如下:

首先要对数据集进行预处理,由于论文的作者来自世界各地,所以名字会有26个字母以外的字符,这些字符在xml文件中定义成变量,并在dtd文档中做了说明,但是在实际解析时这些变量的解析会带来一些问题。为了方便起见,我们可以将用于标识变量的‘&’符号事先替换掉。我用的是linux下的sed命令

sed -i 's/&/,/g' datasets/dblp.xml

然后,创建一个用于保存需要的文档属性的类:

package model;


public class Document {
	public String doc_type;
	public String authors;
	public String doc_title;
	public  int doc_year;
	
	public Document(){
		this.authors = "";
		this.doc_title = "";
		this.doc_year = 0;
	}
		public String getDoc_type() {
		return doc_type;
	}

	public void setDoc_type(String doc_type) {
		this.doc_type = doc_type;
	}

	public String  getAuthors() {
		return authors;
	}

	public void setAuthors(String authors) {
		this.authors = authors;
	}

	public String getDoc_title() {
		return doc_title;
	}

	public void setDoc_title(String doc_title) {
		this.doc_title = doc_title;
	}

	public int getDoc_year() {
		return doc_year;
	}

	public void setDoc_year(int doc_year) {
		this.doc_year = doc_year;
	}

	public String toString(){
		return "type "+doc_type+" #authors: " + authors+
				" #title: " + doc_title + "#year: " + doc_year ;
	}
	
}
这个类可以根据自己的需求来改,如果一篇文档有多个作者,我将作者名拼接成一个字符串,名字之间以字符 ‘|’ 来分隔。
然后写handler:

package handler;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import db.Connector;
import model.Document;

public class SAXParserHandler extends DefaultHandler {
	static Connection conn = Connector.getConn();
	static PreparedStatement pstmt = null;
	int docIndex = 0;
	Document doc = null;
	String value = null;
	boolean isEnd = false;
	public static Set<String> types = new HashSet<String>();
	static {
		try {
			conn.setAutoCommit(false);
			pstmt = conn.prepareStatement("INSERT INTO doc1("
						+ "doc_type, doc_authors, doc_title, doc_year) VALUES ( ?, ?, ?, ?)");
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		types.add("article");
		types.add("inproceedings");
		types.add("proceedings");
		types.add("book");
		types.add("incollection");
		types.add("phdthesis");
		types.add("mastersthesis");
		types.add("www");
	}
	
	//用来遍历xml的开始标签
	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		// TODO Auto-generated method stub
		super.startElement(uri, localName, qName, attributes);
		if(types.contains(qName)) {
			doc = new Document();
			isEnd = false;
			doc.setDoc_type(qName);
		}
	}
	
	//用来遍历xml的结束标签
	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		// TODO Auto-generated method stub
		super.endElement(uri, localName, qName);
		if(qName.equals("dblp")){
			try {
				pstmt.executeBatch();
				conn.commit();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("last batch committed!");
		}else if(types.contains(qName)){
			isEnd = true;
			docIndex++;
			//将Document存入数据库
			try {
				pstmt.setString(1, doc.getDoc_type());
				pstmt.setString(2, doc.getAuthors());
				pstmt.setString(3, doc.getDoc_title());
				pstmt.setInt(4, doc.getDoc_year());
				pstmt.addBatch();
				if (docIndex%50000 == 0){
					System.out.println(docIndex);
					pstmt.executeBatch();
					conn.commit();
				}
			} catch (SQLException e) {
				System.out.println("insert to mysql error!");
				e.printStackTrace();
			}
		}else if (qName.equals("author") && isEnd ==false) {
			if (doc.getAuthors().equals("")){
				doc.setAuthors(value);
			}else
				doc.setAuthors(doc.getAuthors()+"|"+value);
		}else if (qName.equals("title")&& isEnd ==false) {     
			doc.setDoc_title(value);
		}else if (qName.equals("year") && isEnd ==false) {
			doc.setDoc_year(Integer.parseInt(value));
		}
	}
	
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		// TODO Auto-generated method stub
		super.characters(ch, start, length);
		value = new String(ch, start, length);
	}
	
	//用来标识解析开始
	@Override
	public void startDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.startDocument();
		System.out.println("XML parse begin!");		
	}
	
	//用来标识解析结束
	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.endDocument();
		System.out.println("XML parse end!");
	}
	
}

再startElement方法和 characters方法中将数据存入模型,在endElement方法种将数据存入数据库。

说明一下:

我用 isend 来标识一篇文档是否已经遍历结束。在 startElement中遇到article|inproceedings|proceedings|book|incollection|phdthesis|mastersthesis|www 这些标签,将isend置为false,在endElement中遇到这些标签将isend置为true。

由于数据量非常大(500多W条记录),将数据存入数据库时使用批量插入的方式插入,否则会频繁的创建数据库链接,导致内存溢出。

主方法如下:

package parser;

import java.io.IOException;

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

import org.xml.sax.SAXException;

import handler.SAXParserHandler;

public class MyParser {

	public static void main(String[] args) {
		Long start = System.currentTimeMillis();
		// sss
		SAXParserFactory factory = SAXParserFactory.newInstance();
		//通过factory获取SAXParser实例
		try {
			SAXParser parser = factory.newSAXParser();
			//创建SAXParserHandler对象
			SAXParserHandler handler = new SAXParserHandler();
			parser.parse(args[0], handler);
		} 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();
		}
		Long end = System.currentTimeMillis();
		System.out.println("Used: " + (end - start) / 1000 + " seconds");
	}

}

运行时需要添加JVM参数:-Xmx1G -DentityExpansionLimit=2500000

完整的代码已经放到github上: https://github.com/hunanatnjupt/DBLParser  在运行代码之前需要用sql脚本在数据库中创建数据库和表。

作者:u013468917 发表于2016/7/26 8:57:43 原文链接
阅读:4 评论:0 查看评论

相关 [java sax xml] 推荐:

android 或者java SAX解析XML

- - ITeye博客
本实例解析xml使用的是继承DefaultHandler类,此类就是java中封装好的xml解析器类,下面看代码详细说明:. 第一步:首先说明下xml文件,Java Sax解析是按照xml文件的顺序一步一步的来解析,在解析xml文件之前,我们要先了解xml文件的节点的种类,一种是ElementNode,一种是TextNode.

java解析xml数据---sax解析器

- - ITeye博客
下面是handler解析数据的方法. private HashMap map = null;// 存储单个解析的完整对象. private List> list = null;// 存储全部的解析对象. private String currentTag = null; // 正在解析的元素的标签.

xml sax解析

- - 移动开发 - ITeye博客
最近一直在做接口,主要用对xml的解析用的是sax,下面我对sax的几种写法做了一个测试:. System.out.println("耗时:"+(end-start));. System.out.println("当前 Java 虚拟机中的使用内存量:" + (freeMemory01-freeMemory02) + " 字节");.

用java sax处理xml文件(DBLP数据集)

- - CSDN博客综合推荐文章
在java中,可以用多种方式处理xml文件. 前一段时间因为要使用到dblp数据集,而且这个数据集比较大无法一次性加载到内存中解析成文档树再处理. 所以只能用sax的方式边读边处理. 下面是dblp数据集的简介,在处理xml文件之前,对xml的结构的了解很重要:. DBLP是计算机领域内对研究的成果以作者为核心的一个计算机类英文文献的集成数据库系统,按年代列出了作者的科研成果.

使用SAX 解析XMl 通用哟

- - CSDN博客推荐文章
} /** * 设计的数据对象结构 * @author liboliu * */ class Data{. //包含的数据 此数据应该与son互斥. //初始化 root linklist. //获取父对象这里应该 出栈后 在入栈 但是用 linkedList 直接获取栈顶数据 就不用 进行出入栈操作了.

Android XML文档解析(一)——SAX解析

- - CSDN博客移动开发推荐文章
       此文章仅作为学习交流所用.        转载或引用请务必注明原文地址:.        或联系作者:luzhenrong45@gmail.com .     XML:extensible markup language,可扩展标记语言. 与HTML(超文本标记语言,即Hypertext Markup Language)一样,XML也是 标准通用标记语言 (SGML) 的子集,非常适合 Web 传输.

SAX和DOM解析XML区别

- - 移动开发 - ITeye博客
本文转载自: http://blog.csdn.net/zhangxinrun/article/details/5678118. 还有一篇文章总结的也不错: http://www.kaifajie.cn/kecheng/java/7625.html借鉴一下,学习学习. 当你需要处理XML文档时,你的首要选择是使用DOM(文档对象模型)还是使用SAX(用于XML的简单API),即当前使用的两个主要的XML API.

android解析XML总结-SAX、Pull、Dom三种方式

- - CSDN博客移动开发推荐文章
     在android开发中,经常用到去解析xml文件,常见的解析xml的方式有一下三种:SAX、Pull、Dom解析方式. 最近做了一个android版的CSDN阅读器,用到了其中的两种(sax,pull),今天对android解析xml的这三种方式进行一次总结. xml示例(channels.xml)如下:.

Java写xml文件的编码问题

- - CSDN博客推荐文章
最近项目中需要生成xml格式的配置文件,用的是 javax.xml.transform.Transformer 类中提供的transform方法,在本地执行没问题,但是一旦把工程部署到Tomcat下运行,就会出现中文乱码的现象,纠结了许久,在大神的帮助下终于解决了. 有篇文章其实已经讲的很清楚了,链接如下:.

java中多种方式解析xml

- - Java - 编程语言 - ITeye博客
DOM的全称是Document Object Model,也即文档对象模型. 在应用程序中,基于DOM的XML分析器将一个XML文档转换成一个对象模型的集合(通常称DOM树),应用程序正是通过对这个对象模型的操作,来实现对XML文档数据的操作. 通过DOM接口,应用程序可以在任何时候访问XML文档中的任何一部分数据,因此,这种利用DOM接口的机制也被称作随机访问机制.