Java解析与生成XML文档

为了在不同应用软件、不同平台、不同操作系统之间实现数据共享,我们需要XML文件来进行数据的储存和传输。

如下所示为一个xml文件内容,定义了一个书店,包含两本书的信息

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
	<book id="1">
		<name>冰与火之歌</name>
		<author>乔治马丁</author>
		<year>2014</year>
		<price>89</price>
	</book>
	<book id="2">
		<name>安徒生童话</name>
		<year>2004</year>
		<price>77</price>
		<language>English</language>
	</book>
</bookstore>

1、解析XML文件

DOM解析

DOM是XML官方提供的与平台无关的解析方式,DOM会一次性将XML文档读入内存并形成DOM树结构,当XML文档过大时对内存消耗较大,而且读取速度受影响。

值得注意的是DOM不仅将一个<tag></tag>视为一个Node节点,而且节点的属性以及换行的空白也视为Node类型,所以在第一个<book>节点调用childNode.getLength()方法时返回子节点数为9,即包括四个<tag>和5个回车换行空白,但是二者的NodeType不同,可以根据类型进行区分两种节点。

同理<name></name>之间的文字也被视为节点,所以要获取其中的文字需要获取name节点的子节点然后再获取文本值。也可以通过getText(Content)方法来获取name节点间的文本内容,效果相同。

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

public class DOMParse {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
        // 1、创建DocumentBuilderFactory对象
        DocumentBuilderFactory  dbf=DocumentBuilderFactory.newInstance();
        // 2、创建DocumentBuilder对象
        DocumentBuilder db=dbf.newDocumentBuilder();
        // 3、加载xml文件为document对象
        Document doc=db.parse("src/com/xml/books.xml");
        // 获取所有book节点集合
        NodeList bookList=doc.getElementsByTagName("book");
        int num=bookList.getLength();       // 获取节点集合的长度
        for (int i = 0; i < num; i++) {
            Node book=bookList.item(i);     // 获取集合中的一个节点
            NamedNodeMap attrs=book.getAttributes();        // 获取节点的所有属性
            for (int j = 0; j < attrs.getLength(); j++) {       // 遍历节点的属性集
                Node attr=attrs.item(j);        // 获取属性集中的一个属性
                System.out.println(attr.getNodeName());     // 输出属性名
                System.out.println(attr.getNodeValue());        // 输出属性值
            }

            // 知道具体属性名时可直接通过属性名获取属性值
            Element bookElement=(Element)book;      // 此时属性类型从Node转换为Element
            String s=bookElement.getAttribute("id");
            System.out.println(s);

            // 遍历book的所有子节点
            NodeList childNodes=book.getChildNodes();
            for (int j = 0; j < childNodes.getLength(); j++) {
                Node childNode=childNodes.item(j);
                // 根据Node类型区分元素节点和文本节点
                if (childNode.getNodeType()==Node.ELEMENT_NODE){
                    System.out.println("节点名:"+ childNode.getNodeName());
                    // 通过取子节点得到文本值
                    System.out.println("值为:"+childNode.getFirstChild().getNodeValue());
                    // 直接获取文本值
                    System.out.println("文本为:"+ childNode.getTextContent());
                }
            }
        }
    }
}

2、SAX解析

SAX是Java官方提供的基于事件驱动的XML解析方式,与DOM加载整篇xml文档解析的方式不同,SAX是边加载XML文档边解析,对内存耗费小。但与DOM树结构相比,SAX访问数据没有结构,不便于理解和编码,而且由于其方法异步执行,很难同时访问不同数据。

使用SAX过程如下,首先创建一个Factory实例,再通过它创建一个parser实例,之后创建一个继承自DefaultHandler类的handler对象,然后通过parser.parse()进行文档解析,传入两个参数,第一个是文档路径,第二个为handler。

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // 1、获取SAXParserFactory实例
        SAXParserFactory factory = SAXParserFactory.newInstance();
        // 2、获取Parser实例
        SAXParser parser = factory.newSAXParser();
        // 3、创建handler实例
        SAXHandler saxHandler=new SAXHandler();
        // 4、解析文档
        parser.parse("src/com/xml/books.xml",saxHandler);
    }

 handler从文档开始遍历,依次调用调用startDocument()、startElement()、characters()、endElement()、endDocument()方法。

startElement()方法在遇到开始标签例如<book>、<author>时触发,其参数qName为标签名,attributes为标签的属性数组

characters()方法在遇到标签之间的字符串时触发,可以定义对字符串的的操作,这些字符串也包括无意义的空白换行

endELement()方法在遇到结束标签</book>时触发

如果需要在以上各个方法之间共享变量和参数,可以通过定义全局变量来实现

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

public class SAXHandler extends DefaultHandler {
    @Override       // 文档开始执行
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    @Override       // 开始标签执行
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals("book")){      // 解析<book>节点的属性
            System.out.println("======book解析开始======");
            // 根据具体属性名id获取属性值
            String value=attributes.getValue("id");
            System.out.println("book的属性值:"+value);
            // 未知属性值,遍历所有属性
            int num=attributes.getLength();
            for (int i = 0; i < num; i++) {
                System.out.print(attributes.getQName(i)+" : ");     // 获取属性名
                System.out.print(attributes.getValue(i)+"---");     // 获取属性值
            }
        }else {
            System.out.print(qName+" : ");
        }
    }

    @Override        // 遇到字符
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        String s=new String(ch,start,length);
        if (!s.trim().equals("")){      // 排除换行字符串节点
            System.out.print(s+" ");
        }
    }

    @Override       // 结束标签
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if (qName.equals("book")) {
            System.out.println();
            System.out.println("======book解析结束======");
        }
    }

    @Override       // 文档结束执行
    public void endDocument() throws SAXException {
        super.endDocument();
    }
}

 运行结果如下:

3、JDOM

在SAX基础上,第三方开源组织编写了JDOM与DOM4J两种解析方式,因此在使用JDOM时首先需要首先下载和导入jar包。

通过JDOM的getChildren()方法可以很容易的获取标签的子节点并进行遍历

如下所示从XML中读取到的书籍内容保存到Book对象中,并将对象存储到ArrayList列表中

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import com.imooc.entity.Book;        // 引入创建的Book对象


public class JDOMTest {
    // 创建用于保存Book对象的列表
    private static ArrayList<Book> booksList = new ArrayList<Book>();

    public static void main(String[] args) {
        // 进行对books.xml文件的JDOM解析
        // 准备工作
        // 1.创建一个SAXBuilder的对象
        SAXBuilder saxBuilder = new SAXBuilder();
        InputStream in;
        try {
            // 2.创建一个输入流,将xml文件加载到输入流中
            in = new FileInputStream("src/res/books.xml");
            InputStreamReader isr = new InputStreamReader(in, "UTF-8");
            // 3.通过saxBuilder的build方法,将输入流加载到saxBuilder中
            Document document = saxBuilder.build(isr);
            // 4.通过document对象获取xml文件的根节点
            Element rootElement = document.getRootElement();
            // 5.获取根节点下的子节点的List集合,并进行遍历
            List<Element> bookList = rootElement.getChildren();
            for (Element book : bookList) {
                Book bookEntity = new Book();
                System.out.println("======开始解析第" + (bookList.indexOf(book) + 1)+ "书======");
                // 解析book的属性集合
                List<Attribute> attrList = book.getAttributes();
                // //知道节点的属性名称时,获取节点值的方法
                // book.getAttributeValue("id");
                // 遍历attrList(针对不清楚book节点下属性的名字及数量)
                for (Attribute attr : attrList) {
                    // 获取属性名
                    String attrName = attr.getName();
                    // 获取属性值
                    String attrValue = attr.getValue();
                    System.out.println("属性名:" + attrName + "----属性值:"+ attrValue);
                    if (attrName.equals("id")) {
                        bookEntity.setId(attrValue);
                    }
                }
                // 对book节点的子节点的节点名以及节点值的遍历,并将信息保存到Book对象
                List<Element> bookChilds = book.getChildren();
                for (Element child : bookChilds) {
                    System.out.println("节点名:" + child.getName() + "----节点值:"	+ child.getValue());
                    if (child.getName().equals("name")) {
                        bookEntity.setName(child.getValue());
                    }
                    else if (child.getName().equals("author")) {
                        bookEntity.setAuthor(child.getValue());
                    }
                    else if (child.getName().equals("year")) {
                        bookEntity.setYear(child.getValue());
                    }
                    else if (child.getName().equals("price")) {
                        bookEntity.setPrice(child.getValue());
                    }
                    else if (child.getName().equals("language")) {
                        bookEntity.setLanguage(child.getValue());
                    }
                }
                System.out.println("======结束解析第" + (bookList.indexOf(book) + 1)+ "书======");
                booksList.add(bookEntity);
                bookEntity = null;
                System.out.println(booksList.size());
                // 输出保存在列表中的第一个对象的id和name
                System.out.println(booksList.get(0).getId());
                system.out.println(booksList.get(0).getName());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

4、DOM4J

DOM4J也是第三方开源的工具包,与JDOM相比拥有更快的执行速度和灵活性

可以通过elementIterator()方法获取标签的子节点迭代器,从而实现对节点的遍历,其使用方法如下:

package com.imooc.dom4jtest;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class DOM4JTest {
    public static void main(String[] args) {
        // 创建SAXReader的对象reader
        SAXReader reader = new SAXReader();
        try {
            // 通过reader对象的read方法加载books.xml文件,获取docuemnt对象。
            Document document = reader.read(new File("src/res/books.xml"));
            // 通过document对象获取根节点bookstore
            Element bookStore = document.getRootElement();
            // 通过element对象的elementIterator方法获取迭代器
            Iterator it = bookStore.elementIterator();
            // 遍历迭代器,获取根节点中的信息(书籍)
            while (it.hasNext()) {
                System.out.println("=====开始遍历某一本书=====");
                Element book = (Element) it.next();
                // 获取book的属性名以及 属性值
                List<Attribute> bookAttrs = book.attributes();
                for (Attribute attr : bookAttrs) {
                    System.out.println("属性名:" + attr.getName() + "--属性值:"
                            + attr.getValue());
                }
                Iterator itt = book.elementIterator();
                while (itt.hasNext()) {
                    Element bookChild = (Element) itt.next();
                    System.out.println("节点名:" + bookChild.getName() + "--节点值:" + bookChild.getStringValue());
                }
                System.out.println("=====结束遍历某一本书=====");
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

2、生成XML

DOM生成

DOM会在内存中创建DOM树来暂存节点结构,因此如果需要频繁修改DOM结构的话使用DOM比SAX更合适,但相应地,其创建速度是四种方法中最慢的,SAX方法则是最快的。

使用DOM首先还是需要生成document对象,然后通过document创建节点,之后使用父节点添加子节点。

最后通过Transformer对象的transform()方法生成XML文件,transform()需要两个参数,第一个为要输出的源文档,第二个为输出文件流

    public static void main(String[] args) throws ParserConfigurationException, TransformerException {
        // 通过嵌套定义创建一个DOM文档对象doc
        DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
        DocumentBuilder db=dbf.newDocumentBuilder();
        Document doc=db.newDocument();

        // 通过doc创建节点
        Element bookstore=doc.createElement("bookstore");
        Element book=doc.createElement("book");
        book.setAttribute("id","1");        // 为节点添加属性
        Element name=doc.createElement("name");
        name.setTextContent("百年孤独");            // 添加节点之间的文本内容
        // 将子节点添加到父节点
        book.appendChild(name);
        bookstore.appendChild(book);
        doc.appendChild(bookstore);

        // 通过Transformer对象输出xml文件
        TransformerFactory tff=TransformerFactory.newInstance();
        Transformer tf=tff.newTransformer();
        tf.setOutputProperty(OutputKeys.INDENT,"yes");      // 设置输出xml文件换行
        DOMSource domSource=new DOMSource(doc);                     // 要输出的文档对象
        StreamResult streamResult=new StreamResult(new File("src/com/xml/DOMout.xml")); // 输出的目标文件
        tf.transform(domSource,streamResult);
    }

SAX生成

使用SAX生成同样需要首先通过嵌套定义生成一个handler与Transformer对象,通过transformer可以对输出文件进行设置,通过handler可以对文档进行操作。在操作文档之前需要将handler和输出文件流进行关联。

之后通过handler调用相关方法对文档进行编辑,像tag标签一样,startElement/endElement方法成对使用。通过attr对象来设置和添加标签的属性。通过characters()方法来添加标签之间的文本内容。

    public static void main(String[] args) throws TransformerConfigurationException, FileNotFoundException, SAXException {
        // 生成SAX的Transformer对象
        SAXTransformerFactory stf=(SAXTransformerFactory)SAXTransformerFactory.newInstance();
        TransformerHandler handler=stf.newTransformerHandler();
        Transformer transformer=handler.getTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");     // 设置输出属性

        // 创建Result对象并与handler关联
        Result result=new StreamResult(new FileOutputStream(new File("src/com/xml/SAXout.xml")));
        handler.setResult(result);

        // 通过handler编辑xml文件
        handler.startDocument();
        AttributesImpl attr=new AttributesImpl();       // 创建用于编辑节点属性的attr对象
        handler.startElement("","","bookstore",attr);       // 结点开始
        attr.clear();       // 每次新设置一个属性attr之前都需要清空之前的
        attr.addAttribute("","","id","","1");   // 定义attr并添加到节点
        handler.startElement("","","book",attr);
        attr.clear();
        handler.startElement("","","name",attr);
        String s="平凡的世界";
        handler.characters(s.toCharArray(),0,s.length());       // 向节点间添加文本
        handler.endElement("","","name");       // 结束节点
        handler.endElement("","","book");
        handler.endElement("","","bookstore");
        handler.endDocument();
    }

DOM4J生成

DOM4J创建速度虽然慢于SAX,但是快于JDOM,而且其代码较为简洁,是开发中常用的解析与生成方式。

DOM4J创建document对象之后可以直接添加节点,而且由于是封装好的库,代码调用相比之前SAX与DOM方式来说十分简洁。节点的创建和属性的添加等操作只需要一行代码。通过XMLWriter将文档输出,writer()接受两个参数,第一个为文档输出流,第二个可选参数为format对象,通过format可以自动设置好输出的回车换行等空格。

package com.xml;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class DOM4jGenerator {
    public static void main(String[] args) throws IOException {
        // 创建DOM4J文档对象
        Document document=DocumentHelper.createDocument();
        // 从上到下创建节点
        Element bookstore=document.addElement("bookstore");     // 创建并添加子节点
        Element book=bookstore.addElement("book");
        book.addAttribute("id","1");                        // 添加节点属性
        Element name=book.addElement("name");
        name.setText("冰与火之歌");              // 设置标签之间的文本

        OutputFormat format= OutputFormat.createPrettyPrint();      // 设置输出文档的格式
        format.setEncoding("UTF-8");

        // 将文档输出
        FileOutputStream outputStream=new FileOutputStream(new File("src/com/xml/DOM4Jout.xml"));
        XMLWriter writer=new XMLWriter(outputStream, format);
        writer.write(document);
        writer.close();
    }
}

JDOM生成

和DOM4J一样JDOM也是库,不过它需要先new节点,然后通过父节点调用addContent()添加子节点

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class JDOMGenerator {
    public static void main(String[] args) throws IOException {
        Element bookstore = new Element("bookstore");        //创建根节点
        Document document = new Document(bookstore);          // 将根节点添加到document对象

        // 创建并添加子节点
        Element book = new Element("book");
        book.setAttribute("id", "1");    // 为节点添加属性
        bookstore.addContent(book);                 // 父节点添加子节点
        Element name = new Element("name");
        name.setText("冰与火之歌");
        book.addContent(name);

        // 输出文档
        Format format = Format.getPrettyFormat();        // 设置输出格式
        format.setEncoding("GBK");
        XMLOutputter outputter = new XMLOutputter(format);      // 可以传入格式设置对象作为参数
        FileOutputStream outputStream = new FileOutputStream(new File("src/com/xml/JDOMout.xml"));
        outputter.output(document, outputStream);
    }
}
 
发布了124 篇原创文章 · 获赞 65 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/theVicTory/article/details/104160171
今日推荐