Java使用SAX解析XML实战

目前解析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文件可以自行定义。


你可能感兴趣的:(Java)