文章目录

  • 了解xml
  • xml文件的结构
  • **特殊处理**
  • **CDATA**
  • 强烈建议
  • 处理xml文件的几种方式
  • 认识Document对象
  • 解析xml文档
  • 该选择哪种方式解析
  • DOM解析xml文件
  • DOM4j解析xml文档
  • SAX方式解析xml


了解xml

xml全称(Extensible Markup Language)可扩展标记语言。
xml和HTML属于同宗同源,都是SGML的衍生语言,下面是两者之间的区别:

  • xml是大小写敏感的,例如<H1>h和<h1>是不同的xml标签;
  • 在xml中,属性值必须用引号括起来,在HTML中,引号是可有可无的,例如,<applet code="MyApplet.class" width=300>对于HTML来说是合法的,在xml中,必须使用引号,比如<applet width="300">
  • xml中,属性名必须有值,HTML中可以没有值,例如<applet code="MyApplet.class" width>对HTML是合理的,但是对xml来时就是错误的,width属性必须有值,<applet code="MyApplet.class" width=“300”>xml需要注意的是注释中不能有–,否则会报错,如<!--这是错误--的注释-->错误后面的–是不能加的。

xml文件的结构

1、文件头
ML文件头由XML声明与DTD文件类型声明组成。其中DTD文件类型声明是可以缺少的,XML声明:

<?xml version="1.0" encoding="gb2312"?>

XML声明必须出现在文档的第一行。
2、文件体
有元素和元素属性组成,
3、约束文档
这个可有可无,可以提供一个文档类型定义(DTD)或一个XML Schema定义。作用是包含了用于解释文档应如何构成的规则,这些规则指定了每个元素的合法子元素和属性。不能添加指定外的元素和属性。
4. 命名空间

特殊处理

实体引用
实体引用是指分析文档时会被字符数据取代的元素,实体引用用于XML文档中的特殊字符,否则这些字符会被解释为元素的组成部分。例如,如果要显示“<”,需要使用实体引用“<”否则会被解释为一个标记的起始。

CDATA

在XML中由一个特殊的标记CDATA,在CDATA中所有文本都不会被XML处理器解释,直接显示在浏览器中,使用方法如下:
<![CDATA[ 这里的内容可以直接显示。 ]]>

强烈建议

对于属性,一个常用的经验法则是,属性只应用来节点值得解释,而不是用来指定值,如正确的用法是

<font name="姓名" age="年龄">
    <name>张三</name>
    <age>18</age>
</font>

错误的用法:

<font name="张三" age="18">
</font>

处理xml文件的几种方式

DOM,SAX,JDOM和DOM4J等;DOM和SAX是解析方式,JDOM和DOM4J是解析开发包;jsoup也是xml解析开发包,但解析HTML更加方便

认识Document对象

Document对象时xml文档的树形结构在内存中的表现,它由实现了Node接口及其子接口的对象构成

各个子接口的层次结构

xml是java吗 xml和java的区别_XML

解析xml文档

要处理xml文档,就要先解析它。解析器是这样一个程序:它读入一个文件,确认这个文件具有正确的格式,然后将其分解成各种元素,是的程序员能够访问这些元素。java库提供了两种xml解析器:

  • 文档对象模型(Document Object Model,DOM)解析器这样的树形解析器,它们将读入的xml整个文档转换成树结构。
  • XML简单API(Simple API for XML,SAX)解析器这样的流机制解析器,它们在读入xml文档时生成相应的事件。
该选择哪种方式解析

DOM解析器对于实现我们大多数目的来说更容易些,缺点是它需要读取整个文档到内存,需要消耗较多内存,如果文档过大,那就建议使用SAX方式读取文件

DOM解析xml文件

JDK中包含DOM解析器,所以不需要额外的jar包或依赖。
要读入一个XML文档,首先需要一个DocumentBuilder对象,可以从DocumentBuilderFactory中获取
解析前xml文档

<?xml version="1.0" encoding="gb2312"?>
<grandfather>
    <import location="http://localhost:8000/user?wsdl=IUserService.wsdl" namespace="http://webservice.com/">
    </import>
    <parent>
        <son>

        </son>
    </parent>
    <son>
    </son>
</grandfather>

解析代码

try{
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    InputStream resourceAsStream = App.class.getClassLoader().getResourceAsStream("test1.xml");
    Document document = builder.parse(resourceAsStream);
    // getDocumentElement将返回根元素
    Element documentElement = document.getDocumentElement();
    // 通过节点名称获取所有此节点名称的节点
    NodeList sonNodeList = documentElement.getElementsByTagName("son");
    // 构建子节点,此节点可以添加到任意节点下
    Element name = document.createElement("name");
    name.setTextContent("姓名");
    for (int i = 0; i < sonNodeList.getLength(); i++) {
        // 获取指定索引的节点
        Node item = sonNodeList.item(i);
        // 可以用此判断只获取子节点
        if (item instanceof Element){
            Element element = (Element) item;
            System.out.println(element.getTextContent());
            // 添加子节点的方法
            element.appendChild(name);
            System.out.println(i);
        }
    }
    // 获取节点中的文本内容,一般要用trim方法,否则会有换行符
    String textContent = documentElement.getTextContent().trim();
    // 获取指定属性的值
    String location = documentElement.getAttribute("location");
    documentElement.setAttribute("test","测试属性");
    // 返回子节点的集合,此方法如果xml文件中没有DTD文件,则返回的除了子节点外还有节点之间的空格信息
    NodeList childNodes = documentElement.getChildNodes();
    //将构建好的xml输出到指定文件
    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer();;
//             输出内容是否使用换行
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//            输出到文件
    //transformer.transform(new DOMSource(document), new StreamResult(new File("language.xml")));
     File file = new File("language.xml");
//            file.createNewFile();
     FileWriter fileWriter = new FileWriter(file);
     // 之前用的file可以,但是后来就不行了,包文件名、目录名或卷标语法不正确。,改成filewrite就可以了
     transformer.transform(new DOMSource(document), new StreamResult(fileWriter));
}catch (Exception e){
    e.printStackTrace();
}

生成的文档

<?xml version="1.0" encoding="GB2312" standalone="no"?>
<grandfather test="测试属性">
    <import location="http://localhost:8000/user?wsdl=IUserService.wsdl" namespace="http://webservice.com/">
    </import>
    <parent>
        <son>

        </son>
    </parent>
    <son>
    <name>姓名</name>
</son>
</grandfather>

废了很大劲就是没有用代码将生成的xml文件格式化好,如有大佬知道,还望不吝赐教。还有就是name节点只在

DOM4j解析xml文档

需要用到的依赖

<dependency>
  <groupId>dom4j</groupId>
  <artifactId>dom4j</artifactId>
  <version>1.6.1</version>
</dependency>
<!--使用xpath用到的依赖-->
<dependency>
  <groupId>jaxen</groupId>
  <artifactId>jaxen</artifactId>
  <version>1.1-beta-8</version>
</dependency>
try {
   InputStream resourceAsStream = App.class.getClassLoader().getResourceAsStream("test1.xml");
   SAXReader saxReader = new SAXReader();
   Document document = saxReader.read(resourceAsStream);
   // 通过xpath获取节点
   Node node = document.selectSingleNode("/grandfather/parent/son");
   String sonNod = node.getText();
   System.out.println("zijiediandenrirong:" + sonNod);
   // 通过xpath获取节点并获取节点的属性
//            document.
   // 获取根节点
   Element rootElement = document.getRootElement();
   // 获取属性值
   Attribute attribute = (Attribute)rootElement.selectSingleNode("/grandfather/import/@location");
   String value = attribute.getValue();
   System.out.println("属性值" + value);
   // 普通方法获取节点对象直接点element()方法即可,这种只能获取子节点
   Element sonElement = rootElement.element("子节点的节点名称");
   // 获取根节点下面的子节点
   Element anImport = rootElement.element("import");
   // 会获取匹配到的符合条件的节点就返回此节点,即哪个节点靠前就返回哪个节点
   Node sonN = rootElement.selectSingleNode("//son");// 表示不分任何层次结构的选择元素
   String sonNo = sonN.getText();
   System.out.println("22222222:" + sonNo);
   List<Node> sonNode = rootElement.selectNodes("//son");
   for (Node node1: sonNode) {
       String sonNod1 = node1.getText();
       System.out.println("111111111111:" + sonNod1);
   }
   // 添加节点和属性
   Node node1 = rootElement.addElement("person");
   node1.setText("新增加的节点");
   // 设置生成xml的格式,会有层次结构
   OutputFormat xmlFormat = OutputFormat.createPrettyPrint();
   // 没有层次结构,所有节点都在一行
//            OutputFormat compactFormat = OutputFormat.createCompactFormat();
   //设置文件编码
   xmlFormat.setEncoding("UTF-8");
   // 设置换行
//            xmlFormat.setNewlines(true);
   // 生成缩进
//            xmlFormat.setIndent(true);
   // 使用4个空格进行缩进, 可以兼容文本编辑器
   xmlFormat.setIndent("    ");
   //创建写文件方法
   XMLWriter xmlWriter = new XMLWriter(new FileWriter("language.xml"),xmlFormat);
   //写入文件
   xmlWriter.write(document);
   //关闭
   xmlWriter.close();
} catch (Exception e) {
   e.printStackTrace();
}

SAX方式解析xml

此方式只适合获取节点信息,不适合修改xml文件
book实体类

package com.webservice;
/**
 * @author :li
 * @date :Created in 2020/12/12 21:34
 * @description:book实体类
 * @modified By:
 * @version: $
 */
public class Book {
    private String title;
    private String author;
    private Double price;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }
    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

book.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book>
        <title>java基础</title>
        <author>张三</author>
        <price>45</price>
    </book>
    <book>
        <title>C语言</title>
        <author>李四</author>
        <price>33</price>
    </book>
</books>

MySaxHandler解析类

package com.webservice;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;
/**
 * @author :li
 * @date :Created in 2020/12/12 21:24
 * @description:SAX解析器
 * @modified By:
 * @version: $
 */
public class MySaxHandler extends DefaultHandler {

    private List<Book> list = new ArrayList<>();
    private String nodeName;
    private Book book;
    public List<Book> getList() {
        return list;
    }
    public void setList(List<Book> list) {
        this.list = list;
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
//        System.out.println("characters");
        String text = new String(ch, start, length);
        if ("title".equals(nodeName)){
            book.setTitle(text);
        }else if ("author".equals(nodeName)){
            book.setAuthor(text);
        }else if ("price".equals(nodeName)){
            book.setPrice(Double.parseDouble(text));
        }
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
//        System.out.println("endElement");
        if (qName.equals("book")){
            list.add(book);
        }
        /**
         * 此处要设置为空,否则当节点为price时,会调用characters()方法,此时得到的文本内容会是空,而
         * 执行Double.parseDouble(text)回报错
         */
        nodeName = "";
    }
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
//        System.out.println("endDocument");
    }

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
//        System.out.println("startDocument");
    }
    /**
     *
     * @param uri 节点的命名空间 ,对命名空间的详细介绍https://www.runoob.com/xml/xml-namespaces.html
     * @param localName
     * @param qName 节点名称
     * @param attributes 属性实体类
     * @throws SAXException
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
//        System.out.println("startElement");
        System.out.println("uri:" + uri + "===" + "localname:" + localName);
        this.nodeName = qName;
        Book book = null;
        if (qName.equals("book")){
            book = new Book();
            this.book = book;
        }
    }
}

测试类

package com.webservice;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.InputStream;
import java.util.List;
/**
 * @author :li
 * @date :Created in 2020/12/12 18:44
 * @description:通过SAX解析xml文档
 * @modified By:
 * @version: $
 */
public class SAXTest {
    public static void main(String[] args) {
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        try {
            /**
             * 通过打印发现规律
             * 1、重写方法是按照startDocument、startElement、characters、endElement、endDocument执行的,
             *    且执行完所有节点的startElement后才开始执行endElement;
             * 2、每次执行startElement和endElement后就执行characters方法
             * 3、重写的方法的参数都是初始化好的
             */
            InputStream resourceAsStream = SAXTest.class.getClassLoader().getResourceAsStream("book.xml");
            SAXParser saxParser = saxParserFactory.newSAXParser();
            MySaxHandler mySaxHandler = new MySaxHandler();
            saxParser.parse(resourceAsStream,mySaxHandler);
            // 获取解析到的数据
            List<Book> bookList = mySaxHandler.getList();
            for (Book book : bookList) {
                System.out.println("book====" + book );
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}