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对象 |
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() |
得到(此元素只有文本,不含有子元素)文本 |
示例代码如下:
publicclassDom4JHelloWorld { publicvoidparseXMLData() throwsException { // 1.创建一个Dom4j的解析器对象,代表整个dom4j框架SAXReadersaxReader=newSAXReader(); // 2.把XML文件加载到内存中称为一个Document文档对象// Document document = saxReader.read(new File("day15_xml_app\\src\\Contacts.xml"));// 此种通过File对象寻找的方式的缺点:需要通过模块名寻找,一旦模块名被修改,会找不到// getResourceAsStream中的"/"是直接去src下寻找文件InputStreamis=Dom4JHelloWorld.class.getResourceAsStream("/Contacts.xml"); // 通过此种方式找到文件字节输入流,再传递给read方法,即使模块名修改,不影响找到该xml文件Documentdocument=saxReader.read(is); // 获取根元素Elementroot=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
<contactList><contactid="1"vip="true"><name> 潘金莲 </name><gender>女</gender><email>panpan@itcast.cn</email></contact><contactid="2"vip="false"><name>武松</name><gender>男</gender><email>wusong@itcast.cn</email></contact><contactid="3"vip="false"><name>武大狼</name><gender>男</gender><email>wuda@itcast.cn</email></contact><user></user></contactList>
Contact类
publicclassContact { privateintid; privatebooleanvip; privateStringname; privatecharsex; privateStringemail; publicContact() { } publicContact(intid, booleanvip, Stringname, charsex, Stringemail) { this.id=id; this.vip=vip; this.name=name; this.sex=sex; this.emai=email; } publicintgetId() { returnid; } publicvoidsetId(intid) { this.id=id; } publicbooleanisVip() { returnvip; } publicvoidsetVip(booleanvip) { this.vip=vip; } publicStringgetName() { returnname; } publicvoidsetName(Stringname) { this.name=name; } publicchargetSex() { returnsex; } publicvoidsetSex(charsex) { this.sex=sex; } publicStringgetEmail() { returnemail; } publicvoidsetEmail(Stringemail) { this.emai=email; } publicStringtoString() { return"Contact{"+"id="+id+", vip="+vip+", name='"+name+'\''+", sex="+sex+", email='"+emai+'\''+'}'; } }
测试类
publicclassDom4JTest2 { publicvoidparseToList() throwsException { // 1.导入Dom4J框架// 2.创建SAXReader对象SAXReadersaxReader=newSAXReader(); // 3.加载XML文件称为Document对象Documentdocument=saxReader.read(Dom4JTest2.class.getResourceAsStream("/Contacts.xml")); // 4.获取根元素Elementroot=document.getRootElement(); // 5.提取contact子元素List<Element>contactEles=root.elements("contact"); // 6.准备ArrayList集合封装联系人信息List<Contact>contactList=newArrayList<>(); // 7.遍历contact子元素for (ElementcontactEle : contactEles) { // xml文件中的值都是文本,需要使用包装类进行类型转换// 此处不能用强转,需要使用类型转换,强转必须保证被强转的变量类型就是强转后的类型才可以intcontactId=Integer.valueOf(contactEle.attributeValue("id")); booleancontactIsVip=Boolean.valueOf(contactEle.attributeValue("vip")); StringcontactName=contactEle.elementTextTrim("name"); charcontactSex=contactEle.elementTextTrim("gender").charAt(0); StringcontactEmai=contactEle.elementTextTrim("email"); // 8.每一个子元素都是一个联系人对象// 9.把联系人对象加入到contactList集合中contactList.add(newContact(contactId, contactIsVip, contactName, contactSex, contactEmail)); } // 10.遍历contactList集合for (Contactcontact : 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中维护,其他使用到的地方一并修改(核心思想)。
示例代码如下:
电脑类
publicabstractclassComputer { privateStringname; privatedoubleprice; publicComputer() { } abstractvoidstart(); publicStringgetName() { returnname; } publicvoidsetName(Stringname) { this.name=name; } publicdoublegetPrice() { returnprice; } publicvoidsetPrice(doubleprice) { this.price=price; } }
苹果电脑类
publicclassMacextendsComputer{ voidstart() { System.out.println(this.getName() +"苹果电脑启动了"); } }
华为电脑类
publicclassHuaweiextendsComputer{ voidstart() { System.out.println(this.getName() +"华为电脑启动了"); } }
工厂类
publicclassFactoryPattern { /*定义一个方法,创建对象并返回*/publicstaticComputercreatComputer(Stringinfo) { switch (info) { case"华为": Computerc=newHuawei(); c.setName("Huawei pro 16"); c.setPrice(5999); returnc; case"苹果": Computerc2=newMac(); c2.setName("Mac pro 2"); c2.setPrice(11999); returnc2; default: returnnull; } } }
工厂模式测试类
publicclassFactoryDemo { publicstaticvoidmain(String[] args) { Computerc1=FactoryPattern.creatComputer("华为"); c1.start(); // Huawei pro 16华为电脑启动了Computerc2=FactoryPattern.creatComputer("苹果"); c2.start(); // Mac pro 2苹果电脑启动了 } }
5.设计模式:装饰模式
什么是装饰设计模式?
创建一个新类,包装原始类,从而在新类中提升原来类的功能。
装饰设计模式的作用:
作用:装饰模式指的是在不改变原类的基础上,动态地扩展一个类的功能。
装饰模式示例如下图所示。
装饰模式具体步骤如下:
①定义父类。
② 定义原始类,继承父类,定义功能。
③ 定义装饰类,继承父类,包装原始类(在原始类的基础上进行方法改进),增强功能。
示例代码如下:
接口类(共同父类)
publicabstractclassInputStream { publicabstractintread(); publicabstractintread(byte[] buffer); }
原始类
publicclassFileInputStreamextendsInputStream { publicintread() { System.out.println("以低性能的方式读取了一个字节a"); return97; } publicintread(byte[] buffer) { buffer[0] =97; buffer[1] =98; buffer[2] =99; System.out.println("以低性能的方式读取了一个字节数组"+Arrays.toString(buffer)); return3; } }
装饰类
publicclassBufferedInputStreamextendsInputStream { privateInputStreamis; publicBufferedInputStream(InputStreamis) { this.is=is; } publicintread() { System.out.println("提供8kb的缓冲区,提高读取性能"); returnthis.is.read(); } publicintread(byte[] buffer) { System.out.println("提供8kb的缓冲区,提高读取性能"); returnthis.is.read(buffer); } }
测试类
publicclassDecoratorPattern { publicstaticvoidmain(String[] args) { InputStreamis=newBufferedInputStream(newFileInputStream()); System.out.println(is.read()); System.out.println(is.read(newbyte[3])); } }
程序运行结果如下:
提供8kb的缓冲区,提高读取性能
以低性能的方式读取了一个字节a
97
提供8kb的缓冲区,提高读取性能
以低性能的方式读取了一个字节数组[97, 98, 99]
3