目前解析XML的方式有很多,常用的有Dom,jdom,dom4j,SAX等方式解析xml文件。各种方法应用场景不太一致。下面简单介绍一下Dom和SAX解析Xml文件的应用场景。
1、使用Dom解析Xml,每次需要把xml文件整个加载到内存中,然后构建一个驻留内存的树结构,然后代码就可以使用 DOM 接口来操作这个树结构。应用场景如下:
a:xml文件或者xml流内容较少,
b:机器处理能力较强(硬盘,内存较为充足)
c:需要在解析的过程中,对文件内容进行,增、删、改、重新排列等操作。
缺点:将整个文档调入内存(包括无用的节点),浪费时间和空间;
优点:由于整个文档树在内存中,可以方便操作数据(场景c);
2、使用SAX解析XML基于事件驱动。当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,发送事件,程序员编写响应这些事件的代码,保存数据。应用场景如下:
a:只需要从xml文件或者xml流中读取并保存的操作
b:机器可扩展性能有限(硬件资源对程序运行所需要的时间空间要求苛刻)
关于原理和概念,这里不再赘述,直接上代码。
我遇到的应用场景:从xml文件中读取数据,并保存到数据库。
1、自定义SaxHandler实现DefaultHandler,并处理相应的事件,如下:
a:startDocument():文档解析开始时调用,该方法只会调用一次
b:startElement(String uri, String localName, String name, Attributes attributes):标签(节点)解析开始时调用
c:characters(char[] ch, int start, int length) 解析标签内容时调用
d:endElement(String uri, String localName, String name) 标签(节点)解析结束时开始调用
e:endDocument() 文档解析结束时调用一次。
注意:由于项目中没有用到xml文件的标签属性值,在startEmement()中未做处理,如需对属性进行处理,在if(attributes!=null && asset != null){}增加代码
for (int i = 0; i < attributes.getLength(); i++) {
if("id".equals(attributes.getLocalName(i))){
user.setId(Long.parseLong(attributes.getValue(i)));
}
}
即可
public class AssetSaxHandler extends DefaultHandler{
private List assets = null;
private Asset asset;
private String currentTag = null;
private String nodeName = null;
private StringBuilder currentValue;
public List getassets() {
return assets;
}
public AssetSaxHandler(String nodeName) {
this.nodeName = nodeName;
}
@Override
public void startDocument() throws SAXException {
super.startDocument();
assets = new ArrayList();
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, name, attributes);
if (name.equals(nodeName)) {
asset = new Asset();
}
if (attributes != null && asset != null) {
}
currentTag = name;
currentValue = new StringBuilder();
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
super.characters(ch, start, length);
String value = "";
if (currentTag != null && asset != null) {
value = new String(ch, start, length);
if(value != null && !value.trim().equals("") && !value.trim().equals("\n")) {
currentValue.append(value);
}
String nowValue = currentValue.toString();
if(currentTag.equals("AssetName")){
asset.setAssetName(nowValue);
}
else if(currentTag.equals("AssetNumber")){
asset.setAssetNumber(Long.parseLong(nowValue));
}else if(currentTag.equals("MaterialSubtype")) {
asset.setSubCategory(AssetSubCategory.valueOf(nowValue));
}else if(currentTag.equals("MaterialType")) {
asset.setCategory(AssetCategory.valueOf(nowValue));
}else if(currentTag.equals("SerialNumber")) {
asset.setSerialnumber(nowValue);
}else if(currentTag.equals("Tag")) {
asset.setTag(nowValue);
}else if(currentTag.equals("MaterialName")) {
asset.setModel(nowValue);
}else if(currentTag.equals("CabinetAssetName")) {
asset.setCabinetName(nowValue);
}else if(currentTag.equals("CabinetAssetID")) {
asset.setCabinetAssetNumber(nowValue);
}
}
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
super.endElement(uri, localName, name);
if (name.equals(nodeName)) {
assets.add(asset);
asset = null;
currentTag = null;
}
}
代码解决了标签之间的字符的长度超过2048的情况,主要在该方法characters(char[] ch, int start, int length)中。
在该方法中的参数ch是char[]类型的,默认最长长度为2048,如果超过这个长度,将产生一个新的char数组(也是2048长度)来存储数据,于是如果我们需要解析的数据在两个char之间的话(分别在一个的尾部和下一个的头部),那么我将会得到残缺不全的数据,我们的解决方式是用一个StringBuilder对象存储每次读取的值。符合条件就append后面。这里主要看这个方法characters()。在这个方法中处理标签内容。
下面是调用该方法的代码
public static void main(String[] args) {
try {
File file = ResourceUtils.getFile("classpath:AssetsAndHosts.xml");
List assets = getAssets(file,"AssetAndHost");
restClient.saveAssets(assets);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
}
}
/**
*
* @param file
* @param nodeName
* @return
*/
public static List getAssets(File file,String nodeName) {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
AssetSaxHandler handler = new AssetSaxHandler(nodeName);
parser.parse(new FileInputStream(file), handler);
return handler.getassets();
} catch (Exception e) {
// TODO Auto-generated catch block
}
return null;
}
如想测试的话,实体类和xml文件可以自行定义。