遍历是指通过或遍历节点树

遍历节点树

通常,您想要循环一个 XML 文档,例如:当您想要提取每个元素的值时。

这被称为"遍历节点树"。

下面的示例循环遍历所有 <book> 的子节点,并显示它们的名称和值:

<!DOCTYPE html>
<html>
<body> <p id="demo"></p> <script>
var x, i ,xmlDoc;
var txt = "";
var text = "<book>" +
"<title>Everyday Italian</title>" +
"<author>Giada De Laurentiis</author>" +
"<year>2005</year>" +
"</book>"; parser = new DOMParser();
xmlDoc = parser.parseFromString(text,"text/xml"); // documentElement 总是代表根节点
x = xmlDoc.documentElement.childNodes;
for (i = 0; i < x.length ;i++) {
txt += x[i].nodeName + ": " + x[i].childNodes[0].nodeValue + "<br>";
}
document.getElementById("demo").innerHTML = txt;
</script> </body>
</html>

输出:

title: Everyday Italian
author: Giada De Laurentiis
year: 2005

示例解释

  • 将 XML 字符串加载到 xmlDoc
  • 获取根元素的子节点
  • 对于每个子节点,输出节点名称和文本节点的节点值

浏览器中 DOM 解析的差异

浏览器之间存在一些差异。其中一个重要的差异是:

  • 它们如何处理空格和换行符

DOM - 空格和换行符

XML 经常包含节点之间的换行符或空格字符。当文档由简单编辑器(如记事本)编辑时,通常会出现这种情况。

以下示例(由记事本编辑)在每行之间包含 CR/LF(换行符),并在每个子节点之前包含两个空格:

<book>
<title>Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>

Internet Explorer 9 及更早版本不会将空格或换行符视为空格文本节点,而其他浏览器会。

以下示例将输出根元素(books.xml)的子节点数。IE9 及更早版本将输出 4 个子节点,而 IE10 及更高版本以及其他浏览器将输出 9 个子节点:

function myFunction(xml) {
var xmlDoc = xml.responseXML;
x = xmlDoc.documentElement.childNodes;
document.getElementById("demo").innerHTML =
"Number of child nodes: " + x.length;
}

PCDATA - 解析字符数据

XML 解析器通常解析 XML 文档中的所有文本。

当解析 XML 元素时,还会解析 XML 标签之间的文本:

<message>This text is also parsed</message>

解析器执行此操作是因为 XML 元素可以包含其他元素,如此示例中的 <name> 元素包含两个其他元素(first 和 last):

<name><first>Bill</first><last>Gates</last></name>

解析器将其拆分为子元素,如下所示:

<name>
<first>Bill</first>
<last>Gates</last>
</name>

解析字符数据(PCDATA)是一个用于指代将由 XML 解析器解析的文本数据的术语。

CDATA - 未解析的字符数据

术语 CDATA 用于指代 XML 解析器不应解析的文本数据。

字符如 "<" 和 "&" 在 XML 元素中是非法的。

"<" 会生成错误,因为解析器将其解释为新元素的开始。

"&" 会生成错误,因为解析器将其解释为字符实体的开始。

一些文本,比如 JavaScript 代码,包含许多 "<" 或 "&" 字符。为了避免错误,可以将脚本代码定义为 CDATA。

CDATA 部分中的所有内容都会被解析器忽略。

CDATA 部分以 "" 结束:

<script>
<![CDATA[
function matchwo(a,b) {
if (a < b && a < 0) {
return 1;
} else {
return 0;
}
}
]]>
</script>

在上面的示例中,CDATA 部分内的所有内容都会被解析器忽略。

关于 CDATA 部分的注意事项:

  • CDATA 部分不能包含字符串 "]]>"。不允许嵌套 CDATA 部分。
  • 表示 CDATA 部分结束的 "]]>" 不能包含空格或换行符。

XML DOM - 导航节点

可以使用节点之间的关系来导航节点。

导航 DOM 节点

通过节点之间的关系在节点树中访问节点

,通常被称为"导航节点"。

在 XML DOM 中,节点关系被定义为节点的属性:

  • parentNode
  • childNodes
  • firstChild
  • lastChild
  • nextSibling
  • previousSibling

以下图像说明了 books.xml 中的节点树的一部分以及节点之间的关系:

DOM - 父节点

所有节点都有一个父节点。以下代码导航到 <book> 的父节点:

function myFunction(xml) {
var xmlDoc = xml.responseXML;
var x = xmlDoc.getElementsByTagName("book")[0];
document.getElementById("demo").innerHTML = x.parentNode.nodeName;
}

示例解释:

  • 将 books.xml 加载到 xmlDoc
  • 获取第一个 <book> 元素
  • 输出 "x" 的父节点的节点名称

避免空文本节点

某些浏览器可能将空白空格或换行符视为文本节点。

在使用属性如 firstChildlastChildnextSiblingpreviousSibling 时,这会导致问题。

为了避免导航到空文本节点(元素节点之间的空格和换行符),我们使用一个检查节点类型的函数:

function get_nextSibling(n) {
var y = n.nextSibling;
while (y.nodeType != 1) {
y = y.nextSibling;
}
return y;
}

上述函数允许您使用 get_nextSibling(node) 而不是属性 node.nextSibling

代码解释:

  • 元素节点的类型为 1。如果兄弟节点不是元素节点,则移动到下一个节点,直到找到一个元素节点。
  • 获取下一个是元素节点的兄弟节点。

获取第一个子元素

以下代码显示了第一个 <book> 的第一个元素节点:

<!DOCTYPE html>
<html>
<body> <p id="demo"></p> <script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xhttp.open("GET", "books.xml", true);
xhttp.send(); function myFunction(xml) {
var xmlDoc = xml.responseXML;
var x = get_firstChild(xmlDoc.getElementsByTagName("book")[0]);
document.getElementById("demo").innerHTML = x.nodeName;
} // 检查第一个节点是否是元素节点
function get_firstChild(n) {
var y = n.firstChild;
while (y.nodeType != 1) {
y = y.nextSibling;
}
return y;
}
</script> </body>
</html>

输出:

title

示例解释

  • 将 books.xml 加载到 xmlDoc
  • 在第一个 <book> 元素节点上使用 get_firstChild 函数,获取第一个子节点,该子节点是一个元素节点
  • 输出是第一个是元素节点的子节点的节点名称

更多示例

  • lastChild(): 使用 lastChild() 方法和自定义函数获取节点的最后一个子节点。
  • nextSibling(): 使用 nextSibling() 方法和自定义函数获取节点的下一个兄弟节点。
  • previousSibling(): 使用 previousSibling() 方法和自定义函数获取节点的前一个兄弟节点。

XML DOM 获取节点值

nodeValue 属性用于获取节点的文本值。

getAttribute() 方法返回属性的值。

获取元素的值

在 DOM 中,一切都是节点。元素节点没有文本值。元素节点的文本值存储在子节点中,这个节点被称为文本节点。要检索元素的文本值,必须检索元素的文本节点的值。

getElementsByTagName 方法

getElementsByTagName() 方法按照它们在源文档中出现的顺序,返回指定标签名的所有元素的节点列表。假设 books.xml 已加载到 xmlDoc

此代码检索第一个 <title> 元素:

var x = xmlDoc.getElementsByTagName("title")[0];

childNodes 属性

childNodes 属性返回元素的所有子节点的列表。以下代码检索第一个 <title> 元素的文本节点:

x = xmlDoc.getElementsByTagName("title")[0];
y = x.childNodes[0];

nodeValue 属性

nodeValue 属性返回文本节点的文本值。以下代码检索第一个 <title> 元素的文本节点的文本值:

x = xmlDoc.getElementsByTagName("title")[0];
y = x.childNodes[0];
z = y.nodeValue;

结果在 z 中:"Everyday Italian"

完整示例

<!DOCTYPE html>
<html>
<body> <p id="demo"></p> <script>
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myFunction(this);
}
};
xhttp.open("GET", "books.xml", true);
xhttp.send(); function myFunction(xml) {
var xmlDoc = xml.responseXML;
var x = xmlDoc.getElementsByTagName('title')[0];
var y = x.childNodes[0];
document.getElementById("demo").innerHTML = y.nodeValue;
}
</script> </body>
</html>

循环遍历所有 <title> 元素

获取属性值

在 DOM 中,属性也是节点。与元素节点不同,属性节点具有文本值。获取属性值的方式是获取其文本值。

获取属性值 - getAttribute()

getAttribute() 方法返回属性的值。以下代码检索第一个 <title> 元素的 "lang" 属性的文本值:

x = xmlDoc.getElementsByTagName("title")[0];
txt = x.getAttribute("lang");

结果在 txt 中:"en"

循环遍历所有 <book> 元素并获取它们的 "category"

获取属性值 - getAttributeNode()

getAttributeNode() 方法返回属性节点。以下代码检索第一个 <title> 元素的 "lang" 属性的文本值:

x = xmlDoc.getElementsByTagName("title")[0];
y = x.getAttributeNode("lang");
txt = y.nodeValue;

XML DOM 更改节点值

nodeValue 属性用于更改节点的值。

setAttribute() 方法用于更改属性值。

更改元素的值

在 DOM 中,一切都是节点。元素节点没有文本值。元素节点的文本值存储在子节点中,这个节点被称为文本节点。要更改元素的文本值,必须更改元素的文本节点的值。

更改文本节点的值

nodeValue 属性可用于更改文本节点的值。此代码更改第一个 <title> 元素的文本节点值:

xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue = "new content";

示例解释:

  • 假设 books.xml 已加载到 xmlDoc
  • 获取 <title> 元素的第一个子节点。
  • 将节点值更改为 "new content"。

循环遍历并更改所有 <title> 元素的文本节点

更改属性的值

在 DOM 中,属性也是节点。与元素节点不同,属性节点具有文本值。更改属性值的方式是更改其文本值。

使用 setAttribute() 更改属性

setAttribute() 方法更改属性的值。如果属性不存在,则会创建一个新属性。

此代码更改 <book> 元素的 category 属性:

xmlDoc.getElementsByTagName("book")[0].setAttribute("category", "food");

示例解释:

  • 假设 books.xml 已加载到 xmlDoc
  • 获取第一个 <book> 元素。
  • 将 "category" 属性值更改为 "food"。

循环遍历所有 <title> 元素并添加

使用 nodeValue 更改属性

nodeValue 属性是属性节点的值。更改 value 属性会更改属性的值。

xmlDoc.getElementsByTagName("book")[0].getAttributeNode("category").nodeValue = "food";

示例解释:

  • 假设 books.xml 已加载到 xmlDoc
  • 获取第一个 <book> 元素的 "category" 属性。
  • 将属性节点的值更改为 "food"。

XML DOM 删除节点

删除元素节点

removeChild() 方法删除指定的节点。当删除节点时,它的所有子节点也会被删除。

此代码将从加载的 xml 中删除第一个 <book> 元素:

y = xmlDoc.getElementsByTagName("book")[0];
xmlDoc.documentElement.removeChild(y);

示例解释:

  • 假设 books.xml 已加载到 xmlDoc
  • 将变量 y 设置为要删除的元素节点。
  • 使用 removeChild() 方法从父节点中删除元素节点。

删除自己 - 删除当前节点

removeChild() 方法是删除指定节点的唯一方法。当您导航到要删除的节点时,可以使用 parentNode 属性和 removeChild() 方法来删除该节点:

x = xmlDoc.getElementsByTagName("book")[0];
x.parentNode.removeChild(x);

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 将变量 y 设置为要删除的元素节点。
  • 使用 parentNode 属性和 removeChild() 方法删除元素节点。

删除文本节点

removeChild() 方法也可以用于删除文本节点:

x = xmlDoc.getElementsByTagName("title")[0];
y = x.childNodes[0];
x.removeChild(y);

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 将变量 x 设置为第一个 title 元素节点。
  • 将变量 y 设置为要删除的文本节点。
  • 使用 removeChild() 方法从父节点中删除元素节点。

使用 removeChild() 仅仅为了删除节点的文本不是很常见。可以使用 nodeValue 属性代替。请参阅下一段。

清除文本节点

nodeValue 属性可用于更改文本节点的值:

xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue = "";

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 获取第一个 title 元素的第一个子节点。
  • 使用 nodeValue 属性清除文本节点的文本。

通过名称删除属性节点

removeAttribute() 方法按名称删除属性节点。

示例: removeAttribute('category')

此代码将删除第一个 <book> 元素中的 "category" 属性:

x = xmlDoc.getElementsByTagName("book");
x[0].removeAttribute("category");

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 使用 getElementsByTagName() 获取 book 节点。
  • 从第一个 book 元素节点中删除 "category" 属性。

循环遍历并删除所有 <book> 元素的 "category"

通过对象删除属性节点

removeAttributeNode() 方法使用节点对象作为参数删除属性节点。

示例: removeAttributeNode(x)

此代码将删除所有 <book> 元素的所有属性:

x = xmlDoc.getElementsByTagName("book");

for (i = 0; i < x.length; i++) {
while (x[i].attributes.length > 0) {
attnode = x[i].attributes[0];
old_att = x[i].removeAttributeNode(attnode);
}
}

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 使用 getElementsByTagName() 获取所有 book 节点。
  • 对于每个 book 元素,检查是否有任何属性。
  • book 元素中存在属性时,删除属性

XML DOM 添加节点

添加节点 - appendChild()

appendChild() 方法将子节点添加到现有节点。新节点在任何现有子节点之后被添加(追加)。注意:如果节点的位置很重要,请使用 insertBefore()

此代码片段创建一个元素(<edition>),并将其添加在第一个 <book> 元素的最后一个子节点之后:

newEle = xmlDoc.createElement("edition");
xmlDoc.getElementsByTagName("book")[0].appendChild(newEle);

示例解释:

  • 假设 books.xml 已加载到 xmlDoc
  • 创建一个新节点 <edition>
  • 将节点追加到第一个 <book> 元素。

此代码片段与上述相同,但新元素添加了一个值:

newEle = xmlDoc.createElement("edition");
newText = xmlDoc.createTextNode("first");
newEle.appendChild(newText); xmlDoc.getElementsByTagName("book")[0].appendChild(newEle);

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 创建一个新节点 <edition>
  • 创建一个新文本节点 "first"。
  • 将文本节点追加到 <edition> 节点。
  • <edition> 节点追加到 <book> 元素。

插入节点 - insertBefore()

insertBefore() 方法在指定的子节点之前插入一个节点。当添加的节点的位置很重要时,此方法很有用:

newNode = xmlDoc.createElement("book");

x = xmlDoc.documentElement;
y = xmlDoc.getElementsByTagName("book")[3]; x.insertBefore(newNode, y);

示例解释:

  • 假设 books.xml 已加载到 xmlDoc
  • 创建一个新元素节点 <book>
  • 在最后一个 <book> 元素节点之前插入新节点。
  • 如果 insertBefore() 的第二个参数为 null,新节点将在最后一个现有子节点之后添加。x.insertBefore(newNode, null)x.appendChild(newNode) 都将向 x 添加一个新的子节点。

添加新属性

setAttribute() 方法设置属性的值:

xmlDoc.getElementsByTagName('book')[0].setAttribute("edition", "first");

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 为第一个 <book> 元素的 "edition" 属性设置值为 "first"。

注意: 没有名为 addAttribute() 的方法。如果属性不存在,setAttribute() 将创建一个新属性。如果属性已存在,setAttribute() 方法将覆盖现有值。

向文本节点添加文本 - insertData()

insertData() 方法将数据插入现有文本节点。insertData() 方法有两个参数:offset - 开始插入字符的位置(从零开始),string - 要插入的字符串。

以下代码片段将 "Easy" 添加到已加载 XML 的第一个 <title> 元素的文本节点中:

xmlDoc.getElementsByTagName("title")[0].childNodes[0].insertData(0, "Easy ");

XML DOM 克隆节点

克隆节点

cloneNode() 方法创建指定节点的副本。cloneNode() 方法有一个参数(true 或 false)。此参数指示克隆的节点是否应包括原始节点的所有属性和子节点。

以下代码片段复制第一个 <book> 节点并将其附加到文档的根节点:

oldNode = xmlDoc.getElementsByTagName('book')[0];
newNode = oldNode.cloneNode(true);
xmlDoc.documentElement.appendChild(newNode);

结果

Everyday Italian
Harry Potter
XQuery Kick Start
Learning XML
Everyday Italian

示例解释

  • 假设 books.xml 已加载到 xmlDoc
  • 获取要复制的节点(oldNode)。
  • 将节点克隆到 "newNode"。
  • 将新节点附加到 XML 文档的根节点。

最后

为了方便其他设备和平台的小伙伴观看往期文章:

微信公众号搜索:Let us Coding,关注后即可获取最新文章推送

看完如果觉得有帮助,欢迎点赞、收藏、关注

DOM 节点遍历:掌握遍历 XML文档结构和内容的技巧的更多相关文章

  1. 将HTML字符转换为DOM节点并动态添加到文档中

    将HTML字符转换为DOM节点并动态添加到文档中 将字符串动态转换为DOM节点,在开发中经常遇到,尤其在模板引擎中更是不可或缺的技术. 字符串转换为DOM节点本身并不难,本篇文章主要涉及两个主题: 1 ...

  2. Win 10 开发中Adaptive磁贴模板的XML文档结构,Win10 应用开发中自适应Toast通知的XML文档结构

    分享两篇Win 10应用开发的XML文档结构:Win 10 开发中Adaptive磁贴模板的XML文档结构,Win10 应用开发中自适应Toast通知的XML文档结构. Win 10 开发中Adapt ...

  3. 读取XML文档结构并写入内容

    1.在项目中新建XML文档结构.xsd文件,在其中添加相应的节点. 2.读取文档结构并写入内容 string initFileName = @"D:\Config.xml"; Da ...

  4. XML 文档结构必须从头至尾包含在同一个实体内

      XML 文档结构必须从头至尾包含在同一个实体内 CreateTime--2018年4月2日16:40:58 Author:Marydon 问题还原: <![CDATA[ <?xml v ...

  5. Java获取XML节点总结之读取XML文档节点

    dom4j是Java的XML API,用来读写XML文件的.目前有很多场景中使用dom4j来读写xml的.要使用dom4j开发,需要下载导入dom4j相应的jar文件.官网下载:http://www. ...

  6. dom4j 间隔插入节点 处理复杂的xml文档

    前几周跟着老师做了一个小项目,个人主要负责xml文档处理,处理过程还是比较复杂的.此外这篇文章并不是讲基本的dom4j读写xml文档, 所以阅读此文的前提是你已经有了dom4j或jdom等处理xml文 ...

  7. 深入理解DOM节点类型第四篇——文档片段节点DocumentFragment

    × 目录 [1]特征 [2]作用 前面的话 在所有节点类型中,只有文档片段节点DocumentFragment在文档中没有对应的标记.DOM规定文档片段(document fragment)是一种“轻 ...

  8. 【Win10 应用开发】自适应Toast通知的XML文档结构

    老规矩,在开始之前老周先讲个故事. 话说公元2015年7月20日,VS 2015发布.于是,肯定有人会问老周了,C#6有啥新特性,我学不来啊.学不来的话你应该检讨.老周比较保守地计算一下,学会C# 6 ...

  9. XML文档结构

    <?xml version="1.0" encoding="UTF-8"?> <books> <bool id="bk1 ...

  10. 深入理解DOM节点类型第七篇——文档节点DOCUMENT

    × 目录 [1]特征 [2]快捷访问 [3]文档写入 前面的话 文档节点document,隶属于表示浏览器的window对象,它表示网页页面,又被称为根节点.本文将详细介绍文档节点document的内 ...

随机推荐

  1. ubuntu 中 docker 每次都输入 sudo 命令

    查看用户组及成员 sudo cat /etc/group | grep docker 可以添加docker组 sudo groupadd docker 添加用户到docker组 sudo gpassw ...

  2. JVM内存模式

    Java内存模型即Java Memory Model,简称JMM. JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式.JVM 是整个计算机虚拟模型,所以 JMM 是隶属于 JV ...

  3. union和union all的区别?

    一. 显示结果不同 union会自动压缩多个结果集合中的重复结果,而union all则将所有的结果集全部显示出来 二.对重复结果的处理不同 union all是直接连接,取到的是所有值,记录可能有的 ...

  4. Java 多态 解释+案例

    1 package com.bytezreo.duotai; 2 /** 3 * 4 * @Description 面向对象的特征三 ------多态性 5 * @author Bytezero·zh ...

  5. JS3-高级事件

    获取事件和处理事件的第二种方式:事件监听 如何获取和处理鼠标与键盘的基本事件? 事件监听方式 eventTarget.addEventListener(type,listener[,useCaptur ...

  6. 网关接口映射项目 前端 nestjs 项目名称 tf-gateway-http-proxy 改Nginx了

    需求 前端 需要连接后台地址,每次换别人联调都要修改,好几个项目的时候,就要改好几个 关键每次git提交 还会显示文件修改了 强迫症患者 表示 忍不了 群里有人给了个脚本 有时间可以替换nginx h ...

  7. Qt HTTP网络相关GET,POST(HTTP 模拟POST 表单(multipartform)最简单和正式的方法)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  8. .Net实现Html保存到照片

    本文将使用PuppeteerSharp组件.实现Html代码片段生成Jpg照片 PuppeteerSharp项目地址:https://github.com/hardkoded/puppeteer-sh ...

  9. Android线程池封装库

    目录介绍 1.遇到的问题和需求 1.1 遇到的问题有哪些 1.2 遇到的需求 1.3 多线程通过实现Runnable弊端 1.4 为什么要用线程池 2.封装库具有的功能 2.1 常用的功能 3.封装库 ...

  10. 07.Android之多媒体问题

    目录介绍 7.0.0.1 加载bitmap图片的时候需要注意什么?为何bitmap容易造成OOM?如何计算Bitmap占用内存? 7.0.0.2 如何理解recycle释放内存问题?图片加载到内存其实 ...