Java SE基础知识详解第[20]期—XML、XML解析、设计模式

发布于:2022-12-22 ⋅ 阅读:(1169) ⋅ 点赞:(0)

写在前面:

        每一个不曾起舞的日子,都是对生命的辜负。

        希望看到这里的每一个人都能努力学习,不负韶华,成就更好的自己。


        以下仅是个人学习过程中的一些想法与感悟,Java知识博大精深,作为初学者,个人能力有限,哪里写的不够清楚、明白,还请各位不吝指正,欢迎交流与讨论。如果有朋友因此了解了一些知识或对Java有了更深层次的理解,从而进行更进一步的学习,那么这篇文章的意义也就达到了。

目录

1.XML

1.1XML概述

1.2XML的创建、语法规则

1.3XML文档约束方式

2.XML解析技术

2.1XML解析技术概述

2.2Dom4J解析XML文件

3.XML检索技术:Xpath

4.设计模式:工厂模式

5.设计模式:装饰模式


XML、XML解析、设计模式

1.XML

1.1XML概述

XML概述

        XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是是一种数据表示格式,可以用于自定义数据格式,描述非常复杂的数据结构,常用于传输和存储数据。

        XML示例如下图所示。

XML的几个特点和使用场景

        ①纯文本,默认使用UTF-8编码,可嵌套;

        ②如果把XML内容存为文件,那么它就是一个XML文件;

        ③XML的使用场景:XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的配置信息

1.2XML的创建、语法规则

XML的创建

        就是创建一个XML类型的文件,要求文件的后缀必须使用xml,如hello_world.xml

XML的语法规则

        XML文件的后缀名为:xml

        文档声明必须是第一行

        如<?xmversion="1.0" encoding="UTF-8" ?>

        version:XML默认的版本号码、该属性是必须存在的。

        encoding:本XML文件的编码。

XML的标签(元素)规则

        标签由一对尖括号和合法标识符组成:<name></name>,必须存在一个根标签,有且只能有一个

        标签必须成对出现,有开始,有结束:<name></name>

        特殊的标签可以不成对,但是必须有结束标记,如:<br/>

        标签中可以定义属性,属性和标签名空格隔开,属性值必须用双引号引起来<student id = “1”></name>

        标签需要正确的嵌套

XML的其他组成

XML文件中可以定义注释信息:<!– 注释内容 -->

XML文件中某些需要替代的特殊字符

字符

替代值

含义

<

<

小于

>

>

大于

&

&

'

'

单引号

"

"

双引号

        XML文件中可以存在CDATA区<![CDATA[ …内容… ]]>,里面的内容可以随意填写,无需使用字符替代某些特殊字符,IDEA中的快捷键”CD”。

1.3XML文档约束方式

什么是文档约束?

        由于XML文件可以自定义标签,导致XML文件可以随意定义,程序在解析的时候可能出现问题。文档约束是用来限定xml文件中的标签以及属性应该怎么写,以此强制约束程序员必须按照文档约束的规定来编写xml文件

文档约束的分类:DTD、schema

使用文档约束的步骤

        ①:编写schema约束文档,后缀必须是.xsd(编写DTD约束文档,后缀必须是.dtd),具体的形式到代码中观看。

        ②:在需要编写的XML文件中导入该schema(DTD)约束文档

        ③:按照约束内容编写XML文件的标签。

        注:文档约束-DTD可以约束XML文件的编写,不能约束具体的数据类型schema本身也是一个xml文件,本身也受到其他约束文件的要求,所以编写的更加严谨,可以约束具体的数据类型,约束能力上更强大。

2.XML解析技术

2.1XML解析技术概述

XML的数据的作用是什么,最终需要怎么处理?

        存储数据、做配置信息、进行数据传输,最终需要被程序进行读取,解析里面的信息。

什么是XML解析

        使用程序读取XML中的数据。

两种解析方式:SAX解析、DOM解析

        DOM解析解析文档对象模型图示如下图所示。

        其中,整个xml文档称为Document对象;Element对象代表标签(图示蓝色区域);Attribute对象代表属性(图示紫色区域);Text对象代表文本内容(图示黄色区域),且后三者均实现了Node接口。

        Dom解析常用框架:Dom4j:把文件一次性加载到内存中,加载为Document对象再操作。

2.2Dom4J解析XML文件

Dom4J的解析思想?

        得到文档对象Document,从中获取元素对象和内容。

导入Dom4J框架步骤

        ① 下载Dom4j框架,官网下载。 ② 在项目中创建一个文件夹:lib ③ 将dom4j-2.1.1.jar文件复制到 lib 文件夹。④ 在jar文件上点右键,选择 Add as Library -> 点击OK ⑤ 在类中导包使用。

Dom4j解析XML-得到Document对象

SAXReader类构造器

方法名

说明

public SAXReader()

创建Dom4J的解析器对象

SAXReader类得到Document的方法

方法名

说明

Document read(String url)

加载XML文件成为Document对象

Dom4j解析XML的元素、属性、文本

Document类得到根元素的方法

方法名

说明

Element getRootElement()

获得根元素对象

各级元素的方法

方法名

说明

List<Element> elements()

得到当前元素下所有下一级子元素

List<Element> elements(String name)

得到当前元素下指定名字的子元素返回集合

Element element(String name)

得到当前元素下指定名字的子元素,

如果有很多名字相同的返回第一个

String getName()

得到元素名字

String attributeValue(String name)

通过属性名如果有直接得到属性值

String elementText(子元素名)

得到指定名称的子元素

此子元素只有文本,不含有子元素的文本

String getText()

得到此元素只有文本,不含有子元素文本

        示例代码如下:

public class Dom4JHelloWorld {
    @Test
    public void parseXMLData() throws Exception {
        // 1.创建一个Dom4j的解析器对象,代表整个dom4j框架
        SAXReader saxReader = new SAXReader();
        // 2.把XML文件加载到内存中称为一个Document文档对象
//        Document document = saxReader.read(new File("day15_xml_app\\src\\Contacts.xml"));
        // 此种通过File对象寻找的方式的缺点:需要通过模块名寻找,一旦模块名被修改,会找不到
        // getResourceAsStream中的"/"是直接去src下寻找文件
        InputStream is = Dom4JHelloWorld.class.getResourceAsStream("/Contacts.xml");
        // 通过此种方式找到文件字节输入流,再传递给read方法,即使模块名修改,不影响找到该xml文件
        Document document = saxReader.read(is);

        // 获取根元素
        Element root = document.getRootElement();
        System.out.println(root.getName()); // contactList
    }
    
}

        注:getResourceAsStream中的"/"是直接去src下寻找文件,不受模块改名的影响。如InputStream is = Dom4JHelloWorld.class.getResourceAsStream("/Contacts.xml");

XML解析案例

        需求:利用Dom4J的知识,将Contact.xml文件中的联系人数据封装成List集合,其中每个元素是实体类Contact。打印输出 List 中的每个元素。

        示例代码如下:

Contact.xml
<?xmversion="1.0" encoding="UTF-8"?>
<contactList>
    <contact id="1" vip="true">
        <name>   潘金莲  </name>
        <gender>女</gender>
        <email>panpan@itcast.cn</email>
    </contact>
    <contact id="2" vip="false">
        <name>武松</name>
        <gender>男</gender>
        <email>wusong@itcast.cn</email>
    </contact>
    <contact id="3" vip="false">
        <name>武大狼</name>
        <gender>男</gender>
        <email>wuda@itcast.cn</email>
    </contact>
    <user>
    </user>
</contactList>

Contact类

public class Contact {
    private int id;
    private boolean vip;
    private String name;
    private char sex;
    private String email;

    public Contact() {
    }

    public Contact(int id, boolean vip, String name, char sex, String email) {
        this.id = id;
        this.vip = vip;
        this.name = name;
        this.sex = sex;
        this.emai= email;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public boolean isVip() {
        return vip;
    }

    public void setVip(boolean vip) {
        this.vip = vip;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.emai= email;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "id=" + id +
                ", vip=" + vip +
                ", name='" + name + '\'' +
                ", sex=" + sex +
                ", email='" + emai+ '\'' +
                '}';
    }
    
}

测试类

public class Dom4JTest2 {
    @Test
    public void parseToList() throws Exception {
        // 1.导入Dom4J框架
        // 2.创建SAXReader对象
        SAXReader saxReader = new SAXReader();
        // 3.加载XML文件称为Document对象
        Document document = saxReader.read(Dom4JTest2.class.getResourceAsStream("/Contacts.xml"));
        // 4.获取根元素
        Element root = document.getRootElement();
        // 5.提取contact子元素
        List<Element> contactEles = root.elements("contact");
        // 6.准备ArrayList集合封装联系人信息
        List<Contact> contactList = new ArrayList<>();
        // 7.遍历contact子元素
        for (Element contactEle : contactEles) {
            // xml文件中的值都是文本,需要使用包装类进行类型转换
            // 此处不能用强转,需要使用类型转换,强转必须保证被强转的变量类型就是强转后的类型才可以
            int contactId = Integer.valueOf(contactEle.attributeValue("id"));
            boolean contactIsVip = Boolean.valueOf(contactEle.attributeValue("vip"));
            String contactName = contactEle.elementTextTrim("name");
            char contactSex = contactEle.elementTextTrim("gender").charAt(0);
            String contactEmai= contactEle.elementTextTrim("email");
            // 8.每一个子元素都是一个联系人对象
            // 9.把联系人对象加入到contactList集合中
            contactList.add(new Contact(contactId, contactIsVip,
                    contactName, contactSex, contactEmail));
        }
        // 10.遍历contactList集合
        for (Contact contact : contactList) {
            System.out.println(contact);
        }
    }

}

        程序运行结果如下:

Contact{id=1, vip=true, name='潘金莲', sex=女, email='panpan@itcast.cn'}

Contact{id=2, vip=false, name='武松', sex=男, email='wusong@itcast.cn'}

Contact{id=3, vip=false, name='武大狼', sex=男, email='wuda@itcast.cn'}

        注:xml文件中的值都是文本,需要进行类型转换,不能用强转。强转必须保证被强转的变量类型就是强转后的类型向下转型才可以类型转换是不同类型之间的变量的相互转换

        Dom4J的解析后的数据形式通常数据会封装成Java的对象,如单个对象,或者集合对象的形式。

        一键修改所有出现该变量的变量名:Shift + F6。

3.XML检索技术:Xpath

如果需要从XML文件中检索需要的某个信息(如name)怎么解决?

        Dom4j需要进行文件的全部解析,然后再寻找数据。

        Xpath技术更加适合做信息检索。

XPath介绍

        XPath在解析XML文档方面提供了一独树一帜的路径思想,更加优雅,高效。

        XPath使用路径表达式来定位XML文档中的元素节点或属性节点。

使用Xpath检索出XML文件

        需求:使用Dom4J把一个XML文件的数据进行解析。

分析:

        ① 导入jar包(dom4j和jaxen-1.1.2.jar),Xpath技术依赖Dom4j技术

        ② 通过dom4j的SAXReader获取Document对象

        ③ 利用XPath提供的API,结合XPath的语法完成选取XML文档元素节点进行解析操作。

Document中与Xpath相关的API

方法名

说明

Node selectSingleNode("表达式")

获取符合表达式的唯一元素

List<Node> selectNodes("表达式")

获取符合表达式的元素集合

        Xpath的四大检索方案:绝对路径、相对路径、全文检索、属性查找

        XPath:绝对路径采用绝对路径获取从根节点开始逐层的查找/contactList/contact/name节点列表并打印信息:/根元素/子元素/孙元素,从根元素开始,一级一级向下查找,不能跨级。

        XPath:相对路径:采用相对路径获取当前元素下一级contact 节点的name子节点并打印信息:./子元素/孙元素,从当前元素开始,一级一级向下查找,不能跨级。

        XPath:全文搜索:直接全文搜索所有的name元素并打印。

        //contact:找contact元素,无论元素在哪里。

        //contact/name:找name,无论在哪一级,但name一定是contact的子节点。

        //contact//name:contact无论在哪一种,name只要是contact的子孙元素都可以找到。

        XPath:属性查找:在全文中搜索属性,或者带属性的元素。

        //@属性名:查找属性对象,无论是哪个元素,只要有这个属性即可。

        //元素[@属性名]:全文搜索查找含有此属性的元素对象。

        //元素//[@属性名=‘值’]:全文搜索查找含有此属性且属性值值为’值’的元素对象。

4.设计模式:工厂模式

什么是工厂设计模式?

        之前我们创建类对象时,都是使用new对象的形式创建,在很多业务场景下也提供了不直接new的方式。

        工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种获取对象的方式。

工厂设计模式的作用:

        工厂的方法可以封装对象的创建细节,比如:为该对象进行加工和数据注入。

        可以实现类与类之间的解耦操作,只需要在FactoryPattern中维护,其他使用到的地方一并修改(核心思想)。

        示例代码如下:

电脑类

public abstract class Computer {
    private String name;
    private double price;

    public Computer() {
    }

   abstract void start();
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

苹果电脑类

public class Mac extends Computer{

    @Override
    void start() {
        System.out.println(this.getName() + "苹果电脑启动了");
    }
    
}

华为电脑类

public class Huawei extends Computer{

    @Override
    void start() {
        System.out.println(this.getName() + "华为电脑启动了");
    }
    
}

工厂类

public class FactoryPattern {
    /*
        定义一个方法,创建对象并返回
     */
    public static Computer creatComputer(String info) {
        switch (info) {
            case "华为":
                Computer c = new Huawei();
                c.setName("Huawei pro 16");
                c.setPrice(5999);
                return c;
            case "苹果":
                Computer c2 = new Mac();
                c2.setName("Mac pro 2");
                c2.setPrice(11999);
                return c2;
            default:
                return null;
        }
    }

}

工厂模式测试类

public class FactoryDemo {
    public static void main(String[] args) {
        Computer c1 = FactoryPattern.creatComputer("华为");
        c1.start(); // Huawei pro 16华为电脑启动了

        Computer c2 = FactoryPattern.creatComputer("苹果");
        c2.start(); // Mac pro 2苹果电脑启动了
    }
}

5.设计模式:装饰模式

什么是装饰设计模式?

        创建一个新类,包装原始类,从而在新类中提升原来类的功能。

装饰设计模式的作用:

        作用:装饰模式指的是在不改变原类的基础上,动态地扩展一个类的功能

        装饰模式示例如下图所示。

        装饰模式具体步骤如下:

        ①定义父类。

        ② 定义原始类,继承父类,定义功能。

        ③ 定义装饰类,继承父类,包装原始类在原始类的基础上进行方法改进,增强功能

        示例代码如下:

接口类(共同父类)

public abstract class InputStream {
    public abstract int read();

    public abstract int read(byte[] buffer);
}

原始类

public class FileInputStream extends InputStream {

    @Override
    public int read() {
        System.out.println("以低性能的方式读取了一个字节a");
        return 97;
    }

    @Override
    public int read(byte[] buffer) {
        buffer[0] = 97;
        buffer[1] = 98;
        buffer[2] = 99;
        System.out.println("以低性能的方式读取了一个字节数组" + Arrays.toString(buffer));
        return 3;
    }
    
}

装饰类

public class BufferedInputStream extends InputStream {
    private InputStream is;

    public BufferedInputStream(InputStream is) {
        this.is = is;
    }

    @Override
    public int read() {
        System.out.println("提供8kb的缓冲区,提高读取性能");
        return this.is.read();
    }

    @Override
    public int read(byte[] buffer) {
        System.out.println("提供8kb的缓冲区,提高读取性能");
        return this.is.read(buffer);
    }

}

测试类

public class DecoratorPattern {
    public static void main(String[] args) {
        InputStream is = new BufferedInputStream(new FileInputStream());
        System.out.println(is.read());
        System.out.println(is.read(new byte[3]));
    }
    
}

        程序运行结果如下:

提供8kb的缓冲区,提高读取性能

以低性能的方式读取了一个字节a

97

提供8kb的缓冲区,提高读取性能

以低性能的方式读取了一个字节数组[97, 98, 99]

3


写在最后:

        感谢读完!

        纵然缓慢,驰而不息!加油!


网站公告

今日签到

点亮在社区的每一天
去签到