17 NodeList books = root.getChildNodes();
整个XML文件包含在第13行定义的doc对象里,在第15行里,我们通过getDocumentElement方法得到了根节点(也就是books节点),在第17行,通过getChildNoes方法得到该books节点下的所有子节点,随后开始解析整个xml文档。
需要说明的是,在解析前,我们会通过观察xml文档来了解其中的元素名和属性名,所以在后继的代码里,我们会针对元素名和属性名进行编程。
18 if(books!=null){
19 for(int i=0;i<books.getLength();i++){
20 Node book=books.item(i);
21 //获取id属性
22 if(book.getNodeType()==Node.ELEMENT_NODE){
23 String id=book.getAttributes().getNamedItem(“id”).getNodeValue();
24 System.out.println(“id is:” + id);
25 //遍历book下的子节点
26 for(Node node=book.getFirstChild(); node!=null;node=node.getNextSibling()){
27 if(node.getNodeType()==Node.ELEMENT_NODE){
28 //依次读取book里的name,price和memo三个子元素
29 if(node.getNodeName().equals(“name”)){
30 String name=node.getFirstChild().getNodeValue();
31 System.out.println(“name is:” + name);
32 }
33 if(node.getNodeName().equals(“price”)){
34 String price=node.getFirstChild().getNodeValue();
35 System.out.println(“price is:” + price);
36 }
37 if(node.getNodeName().equals(“memo”)){
38 String memo=node.getFirstChild().getNodeValue();
39 System.out.println(“memo is:” + memo);
40 }
41 }
42 }
43 }
44 }
45 }
第19行的for循环里,我们是遍历book元素通过观察xml文件,我们发现book元素出现了2次,所有这个循环会运行两次,而且,book元素有1个id属性,所有我们需要通过第23行的代码,得到id属性的值。
在文档里,book元素有3个子节点,分别是name,price和memo,所以在代码的26行里,再次使用for循环遍历其中的子节点。在遍历时,我们通过29到32行的代码获取到了book元素里name的值,通过类似的代码后继的33到40行代码里得到了price和memo这两个元素的值。
46 } catch (ParserConfigurationException e) {
47 e.printStackTrace();
48 } catch (FileNotFoundException e) {
49 e.printStackTrace();
50 } catch (IOException e) {
51 e.printStackTrace();
52 } catch (SAXException e) {
53 e.printStackTrace();
54 } catch (Exception e) {
55 e.printStackTrace();
56 }
57 //在finally里关闭io流
58 finally{
59 try {
60 input.close();
61 } catch (IOException e) {
62 e.printStackTrace();
63 }
64 }
65 }
66 }
同样地,在解析完成后,在finally从句里,我们关闭了之前用到的IO流(input对象)。
3 基于事件的解析方式
SAX是Simple API for XML的缩写,不同于DOM的文档驱动,它是事件驱动的,也就是说,它是一种基于回调(callback)函数的解析方式,比如开始解析xml文档时,会调用我们自己定义的startDocument函数,从下表里,我们能看到基于SAX方式里的各种回调函数以及它们被调用的时间点。
函数名 | 调用时间点 |
startDocument | 开始解析xml文档时(解析xml文档第一个字符时)会被调用 |
endDocument | 当解析完xml文档时(解析到xml文档最后一个字符时)会被调用 |
startElement | 当解析到开始标签时会被调用,比如在解析“<name>FrameWork</name>”这个element时,当读到开始标签“<name>”时,会被调用 |
endElement | 当解析到结束标签时会被调用,比如在解析“<name>FrameWork</name>”这个element时,当读到结束标签“</name>”时,会被调用 |
characters | 1行开始后,遇到开始或结束标签之前存在字符,则会调用 2两个标签之间,存在字符,则会调用,比如在解析“<name>FrameWork</name>”时,发现存在FrameWork,则会被调用 3标签和行结束符之前存在字符,则会调用 |
从上表里我们能看到characters方法会在多个场合被回调,但我们最期望的调用场景是第2种,这就要求我们最好在解析xml文档前整理下它的格式,尽量避免第1和第3种情况。在ParserXmlBySAX.java这个案例中,我们通过了编写上述的回调函数,实现了SAX方式解析xml文档的功能。
1 //省略import的代码
2 //基于SAX的解析代码需要继承DefaultHandler类
3 public class ParserXmlBySAX extends DefaultHandler{
4 // 记录当前解析到的节点名
5 private String tagName;
6 //主方法
7 public static void main(String[] argv) {
8 String uri = “src/book.xml”;
9 try {
10 SAXParserFactory parserFactory = SAXParserFactory.newInstance();
11 ParserXmlBySAX myParser = new ParserXmlBySAX();
12 SAXParser parser = parserFactory.newSAXParser();
13 parser.parse(uri, myParser);
14 } catch (IOException ex) {
15 ex.printStackTrace();
16 } catch (SAXException ex) {
17 ex.printStackTrace();
18 } catch (ParserConfigurationException ex) {
19 ex.printStackTrace();
20 } catch (FactoryConfigurationError ex) {
21 ex.printStackTrace();
22 }
23 }
在main方法的第8行里,我们指定了待解析xml文档的路径和文件名,在第10行里,我们创建了SAXParserFactory这个类型的SAX解析工厂对象。在第12行,我们通过SAX解析工厂对象,创建了SAXParser这个类型的解析类。在第13行,通过了parse方法启动了解析。
在上文里我们就已经知道,在SAX的方式里,是通过调用各种回调函数来完成解析的,所以在代码里,我们还得自定义各个回调函数,代码如下。
// 处理到文档结尾时,直接输出,不做任何动作
25 public void endDocument() throws SAXException {
26 System.out.println(“endDocument”);
27 }
28 // 处理到结束标签时,把记录当前标签名的tagName设置成null
29 public void endElement(String uri, String localName, String qName) throws SAXException {
30 tagName = null;
31 }
32 // 开始处理文档时,直接输出,不做任何动作
33 public void startDocument() throws SAXException {
34 System.out.println(“startDocument”);
35 }
36 // 处理开始标签
37 public void startElement(String uri, String localName, String name,Attributes attributes) throws SAXException {
38 if (“book”.equals(name)) { //解析book标签的属性
39 for (int i = 0; i < attributes.getLength(); i++) {
40 System.out.println(“attribute name is:” + attributes.getLocalName(i) + " attribute value:" + attributes.getValue(i));
41 }
42 }
43 //把当前标签的名字记录到tagName这个变量里
44 tagName = name;
45 }
46 //通过这个方法解析book的三个子元素的值
47 public void characters(char[] ch, int start, int length)
48 throws SAXException {
49 if(this.tagName!=null){
50 String val=new String(ch,start,length);
51 //如果是name,price或memo,则输出它们的值
52 if(“name”.equals(tagName))
53 { System.out.println(“name is:” + val); }
54 if(“price”.equals(tagName))
55 { System.out.println(“price is:” + val); }
56 if(“memo”.equals(tagName))
57 { System.out.println(“memo is:” + val); }
58 }
59 }
60 }
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
为什么我不完全主张自学?
①平台上的大牛基本上都有很多年的工作经验了,你有没有想过之前行业的门槛是什么样的,现在行业门槛是什么样的?以前企业对于程序员能力要求没有这么高,甚至十多年前你只要会写个“Hello World”,你都可以入门这个行业,所以以前要入门是完全可以入门的。
②现在也有一些优秀的年轻大牛,他们或许也是自学成才,但是他们一定是具备优秀的学习能力,优秀的自我管理能力(时间管理,静心坚持等方面)以及善于发现问题并总结问题。
如果说你认为你的目标十分明确,能做到第②点所说的几个点,以目前的市场来看,你才真正的适合去自学。
除此之外,对于绝大部分人来说,报班一定是最好的一种快速成长的方式。但是有个问题,现在市场上的培训机构质量参差不齐,如果你没有找准一个好的培训班,完全是浪费精力,时间以及金钱,这个需要自己去甄别选择。
我个人建议线上比线下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。
应该学哪些技术才能达到企业的要求?(下图总结)
下的性价比更高,线下培训价格基本上没2W是下不来的,线上教育现在比较成熟了,此次疫情期间,学生基本上都感受过线上的学习模式。相比线下而言,线上的优势以我的了解主要是以下几个方面:
①价格:线上的价格基本上是线下的一半;
②老师:相对而言线上教育的师资力量比线下更强大也更加丰富,资源更好协调;
③时间:学习时间相对而言更自由,不用裸辞学习,适合边学边工作,降低生活压力;
④课程:从课程内容来说,确实要比线下讲的更加深入。
应该学哪些技术才能达到企业的要求?(下图总结)
[外链图片转存中…(img-4KzjKanv-1712091106445)]
[外链图片转存中…(img-itTHKAxo-1712091106445)]