XML3 - XML解析编程

解析方式 简介 优点 缺点
DOM Document Object Model文档对象模型,把整个XML文档先加载到内存中,形成树状结构 节点与节点之间有关系,进行增删改非常方便 如果文档非常大,加载到内存中容易产生内存溢出的问题
SAX Simple API for XML 基于事件驱动的,边读边解析 文档大也不会有内存溢出的问题,查找非常方便 不能进行增删改的操作
解析开发包 简介
JAXP javaSE的一部分,想做增删改,只能用DOM方式。如果SAx,只能做查询
Dom4j 企业都在用,DOM4J提供。增删改查都可以做

JAXP解析

解析方式 解析器工厂类 解析器对象
DOM DocumentBuilderFactory DocumentBuilder
SAX SAXParserFactory SAXParser

JAXP的DOM解析

  1. 由解析器工厂类获取解析器工厂
  2. 解析器工厂产生一个解析器
  3. 解析XML,获得一个Document对象。Document parse(String uri)
  4. 回写
    a 获取回写的工厂类TransformerFactory
    b 获取回写类transformer
    c transform(new DOMSource(document), new StreamResult("src/book2.xml"));
//获取作者的文本内容
public static void run1() throws ParserConfigurationException, SAXException, IOException{
        //获取解析器工厂类
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //获取解析器对象
        DocumentBuilder builder = factory.newDocumentBuilder();
        //解析XML的文档,返回document对象
        Document document = builder.parse("src/book2.xml");
        //获取作者元素对象的集合,返回NodeList
        NodeList nodelist = document.getElementsByTagName("作者");
        //循环遍历,获得每一个作者,打印文本的内容
        for(int i = 0; i < nodelist.getLength(); i++){
            Node node = nodelist.item(i);
            System.out.println(node.getTextContent());
        }
    }

//在第二本下,在末尾添加子节点,需要回写类
public static void run2() throws Exception{
        //获取工厂类
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //获取解析类
        DocumentBuilder builder = factory.newDocumentBuilder();
        //解析XML,返回Document对象
        Document document = builder.parse("src/book2.xml");
        //获取第二本书
        Node book2 = document.getElementsByTagName("书").item(1);
        //创建元素对象
        Element cat = document.createElement("毛");
        //设置文本内容
        cat.setTextContent("我是猫");
        //把元素对象添加到第二本书下
        book2.appendChild(cat);

        //回写
        //创建回写类的工厂
        TransformerFactory transFactory = TransformerFactory.newInstance();
        //获取回写类
        Transformer transformer = transFactory.newTransformer();
        //调用回写的方法
        transformer.transform(new DOMSource(document), new StreamResult("src/book2.xml"));
    }

/**
     * 获取第一本书的属性值
     * @throws Exception 
     */
    public static void run3() throws Exception{
        
        //获取工厂类
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        //解析
        Document document = builder.parse("src/book2.xml");
        //获取作者,再获取第二本书
        Node book1 = document.getElementsByTagName("书").item(0);
        //乡下转型
        Element book = (Element)book1;
        System.out.println(book.getAttribute("编号"));
    }

/**
     * 在作者标签之前,添加团购价标签
     * @throws Exception 
     */
    public static void run1() throws Exception{
        //获取工厂类
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        //解析
        Document document = builder.parse("src/book2.xml");
        //获取作者,再获取第二本书
        Node author = document.getElementsByTagName("作者").item(1);
        Node book2 = author.getParentNode();
        //创建元素
        Element tuan = document.createElement("团购价");
        //设置文本
        tuan.setTextContent("很便宜");
        //加入到作者之前
        book2.insertBefore(tuan, author);
        //回写
        TransformerFactory factory2 = TransformerFactory.newInstance();
        Transformer transformer = factory2.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("src/book2.xml"));
    }

JAXP 的 DOM 封装

public class JaxpDomUtil {
    /**
     * 通过path获取document对象
     * @throws Exception 
     */
    public static Document getDocument(String path) throws Exception{
        //获取工厂类
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //获取解析器对象
        DocumentBuilder builder = factory.newDocumentBuilder();
        //解析XML
        Document document = builder.parse(path);
        return document;
    }
    
    public static void writeXML(Document document,String path) throws Exception{
        //获得回写类工厂
        TransformerFactory factory = TransformerFactory.newInstance();
        //获得回写类
        Transformer transformer = factory.newTransformer();
        //回写
        transformer.transform(new DOMSource(document), new StreamResult(path));
    }
    
    public static void delete() throws Exception{
        //获取文档对象
        Document document = getDocument("src/book2.xml");
        //获取猫
        Node cat = document.getElementsByTagName("毛").item(0);
        //获取书(猫的父节点)
        Node book = cat.getParentNode();
        //通过书删除猫
        book.removeChild(cat);
        //回写
        writeXML(document,"src/boo2.xml");
    }
    
    public static void main(String[] args) throws Exception {
        delete();
    }
    
}

JAXP的SAX解析 只能查询

 由于SAX是基于时间驱动的,所以要有 解析器 和 事件处理器

解析器

  1. 获得解析器的工厂
  2. 获取解析器对象
  3. 解析XML (XML文件地址,事件处理器)
 解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
 事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。

事件处理器

  • 自己编写类,继承DefaultHandler,重写三个方法
    • startDocument()
    • startElement() 重写
    • characters() 重写
    • endElement() 重写
    • endDocument()
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

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



/**
 * SAX的入门
 * @author limaoquan
 *
 */
public class JaxpSaxTest {
    public static void main(String[] args) {
        
    }
    
    /**
     * 获取所有解析的内容
     * @throws Exception 
     * @throws ParserConfigurationException 
     */
    public static void run1() throws ParserConfigurationException, Exception{
        //获取Sax的解析器工厂
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //获取解析器
        SAXParser parser = factory.newSAXParser();
        //解析
        parser.parse("src/book2.xml", new MyHandler());
    }
    
}

/**
 * 获取标签作者的文本内容
 * @author limaoquan
 *
 */
class MyHandler2 extends DefaultHandler{

    private boolean flag = false;
    
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        if("作者".equals(qName)){
            flag = true;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        
        String str = new String(ch, start, length);
        if(flag){
            System.out.println(str);
            }
    }
    
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        flag = false;
    }
    
}



class MyHandler extends DefaultHandler{

    /**
     * 只要一解析到开始标签的时候,默认调用该方法
     */
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        System.out.println("开始标签:"+ qName);
    }

    /**
     * 只要解析到文本内容,默认调用该方法
     */
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        System.out.println("结束标签:"+qName);
    }

    /**
     * 解析到结束标签的时候,默认调用该方法
     */
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        String str = new String(ch, start, length);
        System.out.println(str);
    }
    
}

Dom4j

 Dom4j是一个简单、灵活的开放源代码的库
 使用Dom4j需要下载dom4j相应的jar包
  • 查找标签文本
    • 创建解析器 new SAXReader()
    • 解析xml read()
    • 获取根节点 root = getRootElement()
    • 获取所有指定标签的集合 root.elements(标签名)
    • 返回List集合,可以遍历,或者getIndex() 获取Element对象
    • 获取文本内容 getText()
  • 添加子节点
    • 创建解析器 new SAXReader()
    • 解析xml read()
    • 获取根节点 root = getRootElement()
    • 获取所有指定标签的集合 root.elements(标签名)
    • 直接调用addElement()设置子节点
    • 使用setTest()设置文本
    • 回写xml文件
  • 在指定位置添加子节点
    • 创建元素标签节点 DocumentHelper.createElement()
    • 设置文本 setText()
    • 获取所有指定标签的集合 root.elements(标签名), 返回list
    • 通过list.add(index, element), 在内存中加入子元素
    • 回写xml文件
  • 修改节点文本
    • 找到指定的节点
    • 修改文本内容 setText()
  • 删除节点
    • 找到要删除的节点
    • 通过父节点调用remove()方法删除
/**
     * 获取作者的文本内容
     * @throws Exception 
     */
    public static void run1() throws Exception{
        //获取解析器对象
        SAXReader reader = new SAXReader();
        //解析XML,返回Document对象
        Document document = reader.read("src/book2.xml");
        //获取根节点(书架标签)
        Element root = document.getRootElement();
        //获取书的节点,获取第二本书
        List<Element> books = root.elements("书");
        Element book2 = books.get(1);
        //获取作者的标签
        Element author = book2.element("作者");
        //获取文本内容
        String str = author.getText();
    }
    
    /**
     * 在第二本书下添加子节点
     * @throws Exception 
     */
    public static void run2() throws Exception{
        //获取解析器对象
        SAXReader reader = new SAXReader();
        //解析XML 反回Document对象
        Document document = reader.read("src/book2.xml");
        //获取根节点
        Element root = document.getRootElement();
        //获取第二本书
        Element book2 = (Element)root.elements("书").get(1);
        // 可以直接在第二本书下添加节点,设置文本内容
        book2.addElement("猫").setText("你是猫");
        
        //创建漂亮的格式
        OutputFormat format = OutputFormat.createPrettyPrint();
        
        
        //回写
        XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"),format);
        writer.write(document);
        writer.close();
    }
    
    /**
     * 在第二本书的作者标签之前添加团购价的标签
     * @throws Exception 
     */
    public static void run3() throws Exception{
        //获取解析器对象
        SAXReader reader = new SAXReader();
        //解析XML 反回Document对象
        Document document = reader.read("src/book2.xml");
        //获取根节点
        Element root = document.getRootElement();
        //获取第二本书
        Element book2 = (Element)root.elements("书").get(1);
        //获取书下的所有子节点,返回list集合
        List<Element> list = book2.elements();
        //创建元素对象 DocumentHelper类
        Element dog = DocumentHelper.createElement("狗");
        dog.setText("dagoudagou");
        //list.add(index,Element);
        list.add(1,dog);
        //回写
        XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"));
        writer.write(document);
        writer.close();
    }
    
    /**
     * 删除子节点
     * 删除第二本书下的锚节点
     */
    public static void run4() throws Exception{
        //获取解析器对象
        SAXReader reader = new SAXReader();
        //解析XML 反回Document对象
        Document document = reader.read("src/book2.xml");
        //获取根节点
        Element root = document.getRootElement();
        //获取猫
        Element book2 = (Element)root.elements("书").get(1);
        Element cat = book2.element("猫");
        //通过猫的父节点删除猫
        book2.remove(cat);
        //回写
        XMLWriter writer = new XMLWriter(new FileOutputStream("src/book2.xml"));
        writer.write(document);
        writer.close();
    }

Dom4j获得Document对象的三种方式:

  • 读取xml文档
    SAXReader reader = new SAXReader();
    Document document = reader.read(new File("input.xml"));

  • 解析XML形式的文本
    String text = "<members></members>";
    Document document = DocumentHelper.parseText(text);

  • 主动创建document对象
    Document document = DocumentHelper.createDocument();
    //创建根节点
    Element root = document.addElement("members");

节点对象

  • 获取文档的根节点
    Element root = document.getRootElement();

  • 取得某个节点的子节点
    Element element = node.element("标签名")

  • 获取节点的文字
    String text = node.getText();

  • 设置节点文字
    element.setText();

  • 取得某个节点下所有名为member的子节点,并遍历
    List nodes = rootElm.elements("member");
    for(iterator it = nodes.iterator();it.hasNext(); ){
    Element elem = (Element)it.next();
    //do something ...
    }

  • 对某节点下的所有子节点进行遍历
    for(iterator it = root.elementiterator();it.hasNext(); ){
    Element elem = (Element)it.next();
    //do something ...
    }

  • 在某个节点下添加子节点
    Element ageElm = newMemberElm.addElement("age");

  • 删除某节点
    parentElm.remove(childElm);

  • 添加一个CDATA节点
    Element contenElm = infoElm.addElement("content");
    contentElm.addCDATA(diary.getContent());

节点对象的属性

  • 取得某节点下的某属性
    Element root = document.getRootElement();
    //属性名name
    Attribute attribute = root.attribute("size");

  • 取得属性的文字
    String text = attribute.getText();

  • 删除某属性
    Attribute attribute = root.attribute("size");
    root.remove(attribute);

  • 遍历某节点的所有属性
    Element root = document.getRootElement();
    for(Iterator it = root.attributeIterator(); it.hasNext();){
    Attribute attribute = (Attribute)it.next();
    String text = attribute.getText();
    System.out.println(text);
    }

  • 设置某节点的属性和文字
    newMemberElm.addAttribute("name","sitinspring");

  • 设置属性的文字
    attribute.setText("sitinspring");

将文档写入XML文件

  • 文档中全为英文,不设置编码,直接写入
    XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));
    writer.write(document); //此document为之前建立的Document对象
    writer.close();

  • 文档中有中文,设置编码格式
    OutputFormat format = OutputFormat.createPrettyPrint();
    //指定XML编码
    format.setEncoding("GBK");
    XMLWriter writer = new XMLWriter(new FileWriter("output.xml"),format);
    writer.write(document); //此document为之前建立的Document对象
    writer.close();

Dom4j在指定位置插入节点

Step:

  1. 得到插入位置的节点列表(list)
  2. 调用list.add(index,element), 由index决定element的插入位置
  3. Element元素可以通过DocumentHelpler对象得到
Element aaa = DocumentHelper.createElement("aaa");
aaa.setText("aaa");

List list = root.element("书").elements();
list.add(1,aaa);

//更新document

字符串与XML的转换

  • 字符串转化为XML
    String text = "<members><member>setinsping</member></members>";
    Document document = DocumentHelper.parseText(text);

  • 将文档或节点的XML转化为字符串

SAXReader reader = new SAXReader();
Document document = reader.read(new File("input.xml"));
Element root = document.getRootElement();

String docXmlText = document.asXML();

String rootXmlText = root.asXML();
Element memberElm = root.element("member");
String memberXmlText  = memberElm.asXML();

Dom4j对XPATH的支持

  导入包 jaxen-1.1-beta-6.jar

只能使用Node中的两个方法:

  • selectNodes()
  • selectNode()

Xpath的语法

- /AAA/BBB/CCC    获取的CCC的节点
- //BBB    无论层级关系,所有的BBB都找到
- //DDD/BBB    所有DDD下的BBB元素
- /AAA/CCC/*    CCC下的所有元素
- /*/*/*/BBB      所有有3个祖元素的BBB元素
- //*     所有元素
- /AAA/BBB[1]     AAA下的第一个BBB
- /AAA/BBB[last()]    AAA下最后一个BBB
- //@id    id属性
- //BBB[@name]    选择有name属性的BBB元素
- //BBB[@*]    选择有任意属性的BBB元素
- //BBB[note(@*)]    选择没有属性的BBB元素
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容