美文网首页
Java-XML的解析与生成

Java-XML的解析与生成

作者: 指尖轻敲 | 来源:发表于2018-12-27 00:07 被阅读10次

    XML解析


    零、创建一个XML文件

    如果学过HTML,那么这就看着很轻松了,就是由一个一个标签组成的,开头的第一行是版本和编码的声明,一般不会改变。这里我们创建一个简单的books.xml文件。

    <?xml version="1.0" encoding="UTF-8"?>
    <books>
        <book id="1">
            <name>老人与海</name>
            <author>海明威</author>
        </book>
        <book id="2">
            <name>茶花女</name>
            <author>小仲马</author>
        </book>
    </books>
    

    一、使用DOM解析XML节点

    解析XML文件需要使用几个类,我们直接通过代码来看一下。

    1、加载解析XML文件

    先创建一个DocumentBuilderFactory对象,然后通过该对象的newDocumentBuilder()方法创建一个DocumentBuilder对象,这里需要捕获一下异常。然后通过该对象的parse()方法解析XML文件,该方法接收一个文件路径的参数(默认项目根目录)。

    import org.w3c.dom.*;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    try {
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.parse("books.xml");
    }catch (Exception e){
        e.printStackTrace();
    }
    
    2、获取节点属性

    这里我们要获取每一个book节点,接下来的方法就js的DOM操作差不多了。获取到的所有节点对象一个是NodeList类型。我们可以通过该对象的getLength()方法获取到该节点的个数。

    NodeList bookList = document.getElementsByTagName("book");
    System.out.println(bookList.getLength()); // 2
    

    以上获取到的是一个节点的集合,我们可以通过遍历来获取每一个节点。这里用到的是NodeList对象的item()方法。

    for(int i = 0; i < bookList.getLength(); i++){
        Node book =  bookList.item(i);
    }
    

    上面的xml文件中的每个book节点都有一个id属性,我们来获取它。这里有两种常见的情况,在属性名未知的时候,可以先获取所有属性再进行遍历:

    NamedNodeMap attrs = book.getAttributes();
    for (int j = 0; j < attrs.getLength(); j++){
       Node attr = attrs.item(j);
       System.out.println(attr.getNodeName() + ":" + attr.getNodeValue());
    }
    

    getAttributes()方法获取所有属性是一个NamedNodeMap 类型的对象,然后她也有getLength()方法和item()方法,由此可以获取到该节点的每一个属性,是一个Node类型的对象。最后通过该对象的对应方法获取属性名和属性值。

    感觉好繁琐呀,如果我已经知道这个节点有一个id属性呢?通过节点集合的item方法获取到一个Element类型的对象,这里需要强转一下类型。直接给该对象的getAttribute()方法传递一个属性名参数就可以获取到其属性值。

    Element book  = (Element) bookList.item(i);
    String attrValue = book.getAttribute("id");
    System.out.println(attrValue);
    
    3、获取节点的子节点

    每一个book节点又有子节点,我们来看一下这么获取:通过节点的getChildNodes()方法获取子节点集合对象。同样通过item()方法获取每一个子节点。

    NodeList childNodes = book.getChildNodes();
    for (int j = 0; j < childNodes.getLength(); j++){
        System.out.println(childNodes.item(j).getNodeName()); //打印每一个子节点名
    }
    

    这里注意,空格会被当做是Text类型的节点。打印的节点名永远都是#text。

    节点名称 NodeType nodeName的返回值 nodeValue的返回值
    Element 1 节点的标签名 null
    Attr 2 属性名称 属性值
    Text 3 #text 节点内容

    所以这里我们通过节点类型来判断一下,以便我们获取到真正的标签节点。

    // 只要Element 类型的节点
    if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
        // ...
    }
    
    4、获取节点的内容

    这里也有两种常见的情况,节点中文本内容前没有其他子节点的时候:通过getFirstChild()获取到第一个节点(其实就是文本节点),然后getNodeValue()得到文本节点的内容。这种方法算是碰巧能取到内容。

    String text = childNodes.item(j).getFirstChild().getNodeValue();
    

    如果节点中还有节点怎么,比如上面的name节点改一下:<name><a>书名:</a>老人与海</name>。可以通过getTextContent()方法获取节点下的所有文本内容。直接把标签节点忽略。

    String text2 = childNodes.item(j).getTextContent();
    System.out.println(text2); //书名:老人与海
    

    二、SAX解析xml文件

    SAX解析需要先创建一个继承于DefaultHandler的类。通过重写方法来对XML文件进行解析。

    public class SAXHandler extends DefaultHandler {
        //遍历开始标签
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            super.startElement(uri, localName, qName, attributes);
        }
        //遍历结束标签
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            super.endElement(uri, localName, qName);
        }
    
        //用来标识解析开始
        @Override
        public void startDocument() throws SAXException {
            super.startDocument();
            System.out.println("解析开始了");
        }
        //标识解析结束
        @Override
        public void endDocument() throws SAXException {
            super.endDocument();
        }
        // 可以用来获取节点中的文本
        @Override
        public void characters(char[] ch, int start, int length)   throws SAXException {
            super.characters(ch, start, length);
        }
    }
    
    1、解析属性

    我们在遍历开始标签的startElement方法里,来解析节点。参数qName就是标签名,attributes是所有的属性。

    // 如果开始解析book元素
    if(qName.equals("book")){
        // 获取指定属性的值
        String value = attributes.getValue("id");
        System.out.println(value);
    
        // 遍历属性,获取属性名和属性值
        for(int i = 0; i < attributes.getLength(); i++){
            System.out.println(attributes.getQName(i));
            System.out.println(attributes.getValue(i));
        }
    }
    
    2、解析节点的文本内容

    在characters方法中,第一个参数其实是该文件的所有内容,第二个参数是在解析完一个开始标签之后的位置,第三个参数是开始标签之后到结束标签之前的长度。由此我们可结合这三个参数获取到节点的文本内容。

    String text = new String(ch, start, length);
    // 通过判断过滤一下空格文本节点
    if (!text.trim().equals("")){
        System.out.println(text); //节点中的文本
    }
    
    创建实例调用方法

    然后我们在实际代码中,创建newSAXParser对象,通过的parse方法进行解析,第一个参数是文件路径,第二个参数就是一个刚才创建的SAXHandler类的实例。

    SAXParserFactory spf  = SAXParserFactory.newInstance();
    try {
        SAXParser sp =  spf.newSAXParser();
        SAXHandler sh = new SAXHandler();
        sp.parse("books.xml", sh);
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    

    三、JDOM解析

    JDOM并不是官方提供的方法,所以需要手动添加jar包。通过build方法解析的时候可以传入一个输入流。然后其他就不赘述了,思想都一样,直接代码一看就明白。

    1、属性的解析
    SAXBuilder saxBuilder = new SAXBuilder();
    try {
        FileInputStream fis = new FileInputStream("books.xml");
        Document document = saxBuilder.build(fis);
        // 获取根节点
        Element rootElement = document.getRootElement();
        // 获取根节点下子节点的集合
        List<Element> bookList = rootElement.getChildren();
        // 遍历节点
        for (Element book: bookList) {
            List<Attribute> attributes = book.getAttributes();
            // 遍历属性
            for (Attribute attr: attributes) {
                System.out.println(attr.getName() + ":" + attr.getValue());
            }
            //通过属性名获取属性值
            book.getAttributeValue("id");
        }
    } catch (JDOMException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    2、子节点的解析

    通过getChildren获取子节点,然后通过getName()获取节点名,getValue获取节点中的文本内容。

    List<Element> bookChilds = book.getChildren();
    for (Element ele : bookChilds) {
        System.out.println(ele.getName()+":"+ele.getValue());
    }
     
    name:老人与海
    author:海明威
    name:茶花女
    author:小仲马
    
    3、补充:乱码问题

    如果解析碰到乱码,先修改xml文件的编码格式,如果还是达不到自己想要的效果,我们可以通过在创建输入流的时候指定编码格式。

    4、存到java对象

    首先创建一个book对象,get和set方法就不写了。

    public class Book {
        private String id;
        private String author;
        private String name;
        private String isHot;
    }
    

    然后在遍历属性和子节点的时候,将对应的值添加到Book对象中,遍历完一次之后添加到一个List中,然后清空进行下一次遍历。

    ArrayList<Book> booksList = new ArrayList<Book>();
    SAXBuilder saxBuilder = new SAXBuilder();
    try {
        Document document = saxBuilder.build("books.xml");
        // 获取根节点
        Element rootElement = document.getRootElement();
        // 获取根节点下子节点的集合
        List<Element> bookList = rootElement.getChildren();
        // 遍历节点
        Book b = new Book();
        for (Element book: bookList) {
            List<Attribute> attributes = book.getAttributes();
            for (Attribute attr : attributes) {
                if (attr.getName() == "id"){
                    b.setId(attr.getValue());
                }
                if (attr.getName() == "isHot"){
                    b.setIsHot(b.getIsHot());
                }
            }
            List<Element> bookChilds = book.getChildren();
            for (Element ele : bookChilds) {
                if (ele.getName() == "author"){
                    b.setAuthor(ele.getValue());
                }
                if(ele.getName() == "name"){ 
                    b.setName(ele.getValue());
                }
            }
            booksList.add(b);
            b = null;
        }
        System.out.println(booksList);
    } catch (JDOMException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    四、DOM4J解析

    同样需要手动添加包到项目。

    1、属性解析

    这里不同的是从根节点下获取到一个book节点的遍历器。

    SAXReader reader = new SAXReader();
    // 加载xml文件,获取document对象
    try {
        Document document = reader.read(new File("books.xml"));
        // 获取根节点
        Element books = document.getRootElement();
        // 获取迭代器
        Iterator it = books.elementIterator();
        while (it.hasNext()){
            Element book = (Element)it.next();
            List<Attribute> bookAttrs = book.attributes();
            for(Attribute attr : bookAttrs){
                System.out.println(attr.getName() +":"+ attr.getValue());
            }
        }
    } catch (DocumentException e) {
        e.printStackTrace();
    }
    
    2、解析子节点

    上面从根节点获取到一个book子节点的遍历器,这里同样通过book节点的elementIterator方法获取到一个book节点下子节点的遍历器。

    Iterator itt = book.elementIterator();
    while (itt.hasNext()){
        Element bookChild = (Element) itt.next();
        System.out.println(bookChild.getName());
    }
    

    XML生成


    一、DOM方法

    首先和解析XML文件一样需要创建一些对象

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = null;
    try {
        db = dbf.newDocumentBuilder();
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    }
    Document document = db.newDocument();
    

    创建一个根节点和一个子节点

    Element books = document.createElement("books");
    Element book = document.createElement("book");
    

    给节点添加属性:

    book.setAttribute("id", "0");
    

    给节点添加文本内容:

    book.setTextContent("安娜卡列尼娜");
    

    把子节点插入到根节点,并且把根节点插入到document树:

    books.appendChild(book);
    document.appendChild(books);
    

    最后把document树写进xml文件中:

    TransformerFactory tf = TransformerFactory.newInstance();
    try {
        Transformer transformer = tf.newTransformer();
        // 把节点数写进文件中
        transformer.transform(new DOMSource(document), new StreamResult(new File("myBooks.xml")));
    } catch (TransformerConfigurationException e) {
        e.printStackTrace();
    }catch (TransformerException e) {
        e.printStackTrace();
    }
    

    SAX

    。。。

    DOM4J

    这里先了解一下RSS,它是XML的一种典型应用。是一种描述和同步网站内容的格式。

    // 创建document对象
    Document document = DocumentHelper.createDocument();
    // 创建根节点
    Element rss = document.addElement("rss");
    rss.addAttribute("version", "2.0");
    XMLWriter writer = null;
    try {
        writer = new XMLWriter(new FileOutputStream(new File("rss.xml")));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    try {
        writer.write(document);
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
        }
    }
    

    此时我们已经成功创建了xml文件,

    <?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0"/>
    

    那么接下我们继续创建子节点和内容,调用创建出来的节点对象的addElement就是创建子节点,setText方法是设置内容。

    // 生成子节点和内容
    Element channel = rss.addElement("channel");
    Element title = channel.addElement("title");
    title.setText("我是内容");
    

    此时生成的文件还是没有一个很好的格式,我们来设置一下:

    // 设置想xml格式(缩进、换行、编码)
    OutputFormat format = OutputFormat.createPrettyPrint();
    format.setEncoding("GBK"); // 可以设置编码,默认是UTF-8
    // 在XMLWriter的构造函数中,将format 对象当做第二个参数传入
    writer = new XMLWriter(new FileOutputStream(new File("rss.xml")), format);
    

    这样我们的xml文件的格式就非常美观了

    <?xml version="1.0" encoding="UTF-8"?>
    
    <rss version="2.0">
      <channel>
        <title>我是内容</title>
      </channel>
    </rss>
    

    需要注意,如果有特殊字符的情况下默认会给我们进行转义的,比如

    title.setText("<我是内容");
    // 最后生成时这样的
    <title>&lt;我是内容</title>
    

    想要禁止自动转义也很简单,调用writer对象的setEscapeText()方法设置为false就OK了。

    writer.setEscapeText(false);
    
    //这时生成的内容就是没有转义的
    <title><我是内容</title>
    

    JDOM

    先创建根节点,添加到document。

    // 创建根节点并添加属性,插入到document
    Element rss = new Element("rss");
    rss.setAttribute("version", "2.0");
    Document document = new Document(rss);
    // 将document对象转换成xml文档
    XMLOutputter xo = null;
    try {
        xo = new XMLOutputter();
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        xo.output(document, new FileOutputStream(new File("rssNew.xml")));
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    创建子节点及子节点的文本,

    Element channel = new Element("channel");
    rss.addContent(channel);
    Element title = new Element("title");
    title.setText("我是内容");
    channel.addContent(title);
    

    设置格式,也是创建一个format对象然后传入XMLOutputter的构造函数。

    Format format = Format.getCompactFormat();
    format.setIndent(""); //设置换行
    format.setEncoding("GBK"); //设置编码
    
    XMLOutputter xo = new XMLOutputter(format);
    

    相关文章

      网友评论

          本文标题:Java-XML的解析与生成

          本文链接:https://www.haomeiwen.com/subject/qzxllqtx.html