一、XML


1.XML:传输数据和保存数据
      特点:有且只有一个根元素,xml结构为树形结构

2.XML文档结构分析(一切皆节点)
     接口Node  
          子接口 
             Document :描述所有的xml文件
               Element  :描述所有的元素
               Text     :描述xml所有的文本内容(换行也是文本内容)
               Attr     :描述xml所有属性
3.xml的CDATA
       用CDATA括起来的内容不会被xml文件解析,而是当成字符串使用。
        <![CDATA[
          中间的内容就是字符串<br>
    ]]>

二、XML解析

1.DOM解析(解析小型文件)

一次性将整个XML文件加载到内存中,形成一颗DOM树对象,操作xml,
实际上就是操作(增删改查)DOM对象即可, 操作完毕以后,要执行同步操作

     ①获取Document文档对象:
          1.创建DocumentBuilderFactory对象
        DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
          2.创建DocumentBuilder对象
         DocumentBulider builder=factory.newDocumentBuilder();
          3.创建Document对象
                Document doc=builder.parse(f);
            Document doc=builder.newDocument();

       ②常用API
           Node接口: 
            getTextContent():获取文本内容
        setTextContent():设置文本内容
        appendChild(Node newChild):添加子元素
                removeChild(Node oldChild):删除子元素

           Document接口:
                getDocumentElement():获取根元素
        getElementsByTagName(""):根据指定的标签名获取所有子元素
        createElement(""):创建元素

           Element接口:(可以设置文本内容)
                getElementByTagName(""):获取所有子元素
        setAttribute("","");设置属性和属性值
        getAttribute(""):获取属性值

      ③修改后,要执行同步操作:
         同步操作核心类:Transformer
      Transformer trans=TransformerFactory.newInstance().newTransformer();
       执行同步方法:
           trans.transform(Source xmlSource,Result outputTarget);
         xmlSource使用DOMSource
         outputTarget使用StreamResult
           trans.transform(new DOMSource(doc),new StreamResult(new File(filePath)));
public void testGet() throws Exception {

        // 步骤
        // 1, 创建Document文档对象
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File(filePath));
        // 2,获取根元素 <contacts>
        Element root = doc.getDocumentElement();
        System.out.println(root);
        // 3,获取第二个联系人<linkman id="2">
        NodeList linkmans = root.getElementsByTagName("linkman");
        Element linkmanEL = (Element)linkmans.item(0);
        // 4,获取第二个联系人的<name>元素
        Node nameEL = linkmanEL.getElementsByTagName("name").item(0);
        // 5, 获取<name>元素的文本内容
        String name = nameEL.getTextContent();
        System.out.println(name);
        //6, 修改<name>的文本内容
        nameEL.setTextContent("qf@qq.com");
        //7, 创建联系人元素 <linkman>
        Element linkmanEL = doc.createElement("linkman");
//      8,创建联系人的子元素 <name><email><address><group>
        Element nameEL = doc.createElement("name");
        Element emailEL = doc.createElement("email");

//      9, 设置子元素对应的文本内容
        nameEL.setTextContent("虚竹");
        emailEL.setTextContent("xu@qq.com");

//      10,将子元素添加到<linkman>
        linkmanEL.appendChild(nameEL);
        linkmanEL.appendChild(emailEL);

//      11,将<linkman>元素添加到根元素<contacts>中
        root.appendChild(linkmanEL);
//      12, 同步操作
        TransformerFactory factory = TransformerFactory.newInstance(); 
        Transformer trans = factory.newTransformer();
        //同步数据的数据来源
        Source xmlSource = new DOMSource(doc);
        //同步数据的目标
        Result outputTarget = new StreamResult(new File(filePath));
        trans.transform(xmlSource, outputTarget);
    }

1.2 使用dom4j框架解析xml文件

             //需求:获取第一个联系人的名称
@Test
public void testGetName() throws Exception {
    //1,获取Document对象
    SAXReader reader = new SAXReader();
    String filePath = "C:/Users/YL/workspace/j1704/day05-xml-parse/contacts.xml";
    Document doc = reader.read(new File(filePath));
    //2. 获取根元素
    Element root = doc.getRootElement();
    //3,获取第一个联系人 <linkman id="1">
    List linkmans = root.elements("linkman");
    Element linkmanEL = (Element)linkmans.get(0);
    //4,获取<name>元素
    Element nameEL = linkmanEL.element("name");
String name = nameEL.getText();
    String name = linkmanEL.elementText("name");
    //5.获取第一联系人的id属性
    Attribute attr = linkmanEL.attribute("id");
    System.out.println(attr.getName()+" ---- "+attr.getValue());
    System.out.println(name);
}

             //添加一个 联系人
             @Test
public void testAdd() throws Exception {
    String filePath = "C:/Users/YL/workspace/j1704/day05-xml-parse/contacts.xml";
    //1,获取Document文档和根元素
    SAXReader reader = new SAXReader();
    Document doc = reader.read(new File(filePath));
    Element root = doc.getRootElement();
    //2,创建一个linkman联系人
    Element linkmanEL = root.addElement("linkman").addAttribute("id", "4");
    //3. 创建linkman联系人的子元素
    linkmanEL.addElement("name").addText("王姑娘");
    linkmanEL.addElement("email").addText("xu@qq.com");
    linkmanEL.addElement("addres").addText("河南");
    linkmanEL.addElement("group").addText("少林");
    //4. 同步操作(将修改的数据同步到磁盘)
    //----------------普通的同步方法,同步到磁盘没有格式-------------------
    // 1)创建FiewWrite对象
    //FileWriter writer = new FileWriter(filePath);
    //2) 创建 XMLWrite 对象,用于同步Document文档
     //XMLWriter xmlWriter = new XMLWriter(writer);
     //3)同步数据
     //xmlWriter.write(doc);
     //3)关闭流:一定要关闭
     //xmlWriter.close();
    //--------------漂亮的同步方法:有格式------------------
    //1) 创建格式化对象
     OutputFormat format = OutputFormat.createPrettyPrint();
     //2)创建FiewWrite对象
     FileWriter writer = new FileWriter(filePath);
     //3)创建 XMLWrite 对象,用于同步Document文档
     XMLWriter xmlWriter = new XMLWriter(System.out, format);
     //4)同步数据
     xmlWriter.write(doc);
     //5)关闭流:一定要关闭
     xmlWriter.close();
}

2.SAX解析

SAX(simple API for XML):是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。而且相比于DOM,SAX可以在解析文档的任意时刻停止解析,但任何事物都有其相反的一面,对于SAX来说就是操作复杂。
  SAX是事件驱动型XML解析的一个标准接口不会改变 SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

大多数SAX都会产生以下类型的事件:
1.在文档的开始和结束时触发文档处理事件。
2.在文档内每一XML元素接受解析的前后触发元素事件。
3.任何元数据通常由单独的事件处理
4.在处理文档的DTD或Schema时产生DTD或Schema事件。
5.产生错误事件用来通知主机应用程序解析错误。
需求:解析contacts.xml文件,将所有的linkman联系人封装到LinkMan对象中并且添加到list和中

SAX解析的核心类 : SAXParser

如何获取SAXParser 对象?

1, 创建 SAXParserFactory 对象
 SAXParserFactory factory = SAXParserFactory.newInstance();
2, 创建 SAXParser对象
 SAXParser  parser = factory.newSAXParser();
3, 进行SAX解析
 parser.parse(File f, DefaultHandler hb) 
f : 需要解析 xml文件
 hb : 解析器,真正干活解析的代码全在 DefaultHandler 中
public class SAXParseTest {
    String filePath = "C:/Users/YL/workspace/j1704/day05-xml-parse/contacts.xml";
    //需求:解析contacts.xml文件,将所有的linkman联系人封装到LinkMan对象中并且添加到list和中
    @Test
    public void testParse() throws Exception {
//      1, 创建 SAXParserFactory 对象
        SAXParserFactory factory = SAXParserFactory.newInstance();
//      2, 创建 SAXParser对象
        SAXParser parser = factory.newSAXParser();
//      3, 进行SAX解析
        //创建处理器对象
        CustomeHanlder dh  = new CustomeHanlder();
        parser.parse(new File(filePath), dh);
        //获取处理器解析的list集合
        List<LinkMan> list = dh.getList();
        for (LinkMan linkMan : list) {
            System.out.println("解析结果:"+linkMan);
        }
    }
public class CustomeHanlder extends DefaultHandler {
    // 创建list集合,存放联系人对象
    @Getter
    private List<LinkMan> list = new ArrayList<>();
    //联系人对象
    private LinkMan linkman;

    //保存上一次元素名称
    private String preTag;

    // 元素开始时候调用
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        // 判断元素名称是linkman时创建对象
        if ("linkman".equals(qName)) {
            linkman = new LinkMan();
            // 获取id属性
            String id = attributes.getValue("id");
            linkman.setId(Integer.valueOf(id));
        }
        //将qName 赋值给 成员变量 preTage ,在解析文本的时候就能获取上一次的元素名称
        preTag = qName;
    }
    // 解析文本内容调用
    public void characters(char[] ch, int start, int length) throws SAXException {
        String value = new String(ch,start,length);

        //排除所有为空的文本,换行等等
        if(value!=null){
            if("name".equals(preTag)){
                linkman.setName(value);
            }else if("email".equals(preTag)){
                linkman.setEmail(value);
            }else if("address".equals(preTag)){
                linkman.setAddress(value);
            }else if("group".equals(preTag)){
                linkman.setGroup(value);
                //此时linkman对象所有的数据都封装完毕可以添加list中去了
                list.add(linkman);
            }
        }
    }
    // 元素结束时候调用
    public void endElement(String uri, String localName, String qName) throws SAXException {
        preTag = null;
    }

    // 文档开始时候调用
    public void startDocument() throws SAXException {

    }

    // 文档结束时候调用
    public void endDocument() throws SAXException {
    }
}