第13章 基于XPath的标记数据选取

13.1 设计需求

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?>
<books>
    <book>
        <ISBN>8475484848</ISBN>
        <title>XML技术</title>
        <price>80</price>
    </book>
    <book>
        <ISBN>35235348</ISBN>
        <title>Python programming</title>
        <price>65</price>
    </book>
    <book>
        <ISBN>67676348</ISBN>
        <title>Java development</title>
        <price>76</price>
    </book>
    <book>
        <ISBN>27974348</ISBN>
        <title>NodeJS development</title>
        <price>90</price>
    </book>
</books>

现提出如下设计需求:

  1. 设计合适的用户接口,用户可自定义输入XPath。
  2. 用户可查看XML文档原始数据。
  3. 用户可执行"XPath"语句并输出XML文档片断。

13.2 设计分析

13.2.1 功能分析

测试用户期望功能如下:

  1. 测试用户可自定义输入XPath。
  2. 测试用户可查看测试XML文档原始数据。
  3. 测试用户可执行自定义的"XPath"语句并输出相应XML文档片断。

用户测试功能用例建模如图13.1所示。

用户测试功能用例建模
图13.1 用户测试功能用例建模

13.2.2 核心业务数据流分析

功能模块中的核心业务是:执行XPath并返回XML文档片断。

核心业务流程描述如下:

  1. 测试用户提交动态输入的XPath。
  2. 后台获取XPath及XML文档对象。
  3. 后台对XML文档对象进行基于XPath的标记选取返回XML文档片断。

核心业务数据流建模如图13.2所示。

核心业务数据流建模
图13.2 核心业务数据流建模

13.2.3 文件包及文件设计分析

基于三层架构视角的文件包及文件设计如表13.1所述。

序号 包设计 文件设计
1 设计表示层包(包名:UI),抽象表示层文件容器。 (1) 设计界面主页文件main.js。
(2) 主页引入jquery框架(jquery.js),辅助表示层处理。
2 设计业务逻辑层包(包名:server),抽象业务层文件容器。 (1) 设计通用业务逻辑服务文件server.js。
(2) 设计当前模块专用业务逻辑服务文件server_13.js。
(3) 引入xmldom模块(xmldom.js,外部安装获得)。
(4) 引入xpath模块(xpath.js,外部安装获得)。
3 设计数据层包(包名:data),抽象数据层文件容器。 (1) 设计XML数据存储文件books.xml。

文件包设计及文件关系如图13.3所示。

文件包设计及文件关系
图13.3 文件包设计及文件关系

13.3 文件架构

文件架构如图13.4所示。

文件架构
图13.4 文件架构

文件及文件夹功能说明见表13.2所述。

序号 文件(夹)名 作用
1 books.xml 用于测试的XML基础数据
2 Server.js NodeJS后台通用逻辑服务程序,动态引入Server_13.js模块。
3 jquery-1.11.1.js JQuery函数库,辅助表示层数据可视化。
4 main.html 实现人机交互的用户界面主页
5 data 存放XML数据文件的文件夹
6 server 存放业务逻辑层文件的文件夹
7 UI 存放表示层文件的文件夹
8 xmldom 安装xmldom模块形成的文件夹
9 xpath 安装xpath模块形成的文件夹
10 Server_13.js 当前模块后台服务专用逻辑

说明:文件夹data、server、UI的父级文件夹为"13"。

13.4 代码实现

13.4.1 主页实现

对应文件:main.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h2>基于XPath的标记数据选取测试</h2>
    <hr />
    请输入XPath<input type="text" id="path_input" value="/books/book/title" style="width:400px" />
    <input type="button" value="按当前XPath选取数据" onclick="select_TagData()" />
    <hr />
    <div id="outer_div" style="width:654px">
        <div id="load_Full_XML" style="width:320px;height:400px; margin-right:10px;float:left;border:1px solid black">
            <input type="button" value="加载XML文档全文" onclick="loadXML()" /><br />
            <textarea id="fullXML_ta" style="width:310px;height:360px"></textarea>
        </div>
        <div id="select_result" style="width: 320px; height: 400px; float: left; border: 1px solid black ">
            标记数据选取结果:<hr style="margin:0" />
            <textarea id="resultXML_ta" style="width:310px;height:360px"></textarea>
        </div>
    </div>
</body>
</html>

<script type="text/javascript" src="jquery-1.11.1.js"></script>
<script type="text/javascript">
    //加载XML全文
    function loadXML() {
        var xhttp = new XMLHttpRequest();
        xhttp.open("post", "http://localhost:1007/viewAllbooks", true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                $("#fullXML_ta").val(xhttp.responseText);
            }
        }
        xhttp.send();
    }

    //基于XPath的标记数据选择
    function select_TagData() {
        var xpath = $("#path_input").val();
        var xhttp = new XMLHttpRequest();
        xhttp.open("post", "http://localhost:1007/exeXPath?param=" + encodeURIComponent(xpath), true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                $("#resultXML_ta").val(xhttp.responseText);
            }
        }
        xhttp.send();
    }
</script>

13.4.2 后台服务实现

1、通用服务逻辑

对应文件:server.js

// JavaScript source code
//------------请将这个文件保存为Unicode格式,否则麻烦大大的----------

const http_obj = require("http");//用于产生服务对象
const fs_obj = require("fs");//读写后台文件
const url_tran_obj = require("url");//解释URL

const server = http_obj.createServer(function (request, response) {
    //请求路径处理
    var req_str = decodeURI(request.url);
    var req_head;
    var POS = req_str.indexOf("?");
    if (POS == -1) { req_head = req_str; }
    else { req_head = req_str.substring(0, POS); }

    //设置查询对象
    var url_obj = url_tran_obj.parse(req_str, true);
    var Q_obj = url_obj.query;

    //响应头设置
    response.setHeader("Content-Type", "text/plain;charset=utf-8");
    response.setHeader("Access-Control-Allow-Origin", "*");//实现跨域访问
    response.writeHead(200);

    //%%%%%%%%%%%%%------------begin your code
    require("./13/server/server_13.js")(request, response, req_head, Q_obj, fs_obj);
    //%%%%%%%%%%%%%------------end your code
});

server.listen(1007);
console.log("Server is running at port 1007...");

2、专用服务逻辑

文件server_13.js实现代码:

function server_13(request, response, req_head, Q_obj, fs_obj) {
    if (req_head == "/viewAllbooks_13") {
        //代码1
        fs_obj.readFile("./13/data/books.xml", "utf-8", function (err, data) {
            if (err) { response.end(err); }
            else { response.end(data); }
        });
    }

    if (req_head == "/exeXPath_13") {
        //代码2
        var b_xpath = Q_obj["param"];
        var select = require("./xpath");//获取函数
        var dom = require("./xmldom").DOMParser;//获取类
        fs_obj.readFile("./13/data/books.xml", "utf-8", function (err, data) {
            if (err) { response.end(err); }
            else {
                var xmldom = new dom().parseFromString(data);
                var nodes = select(xmldom, b_xpath);
                response.end(nodes.toString());
            }
        });
    }
}

module.exports = server_13

13.4.3 实现效果

加载XML文档全文如图13.5所示。

执行当前XPath选取数据如图13.6所示。

加载XML文档全文
图13.5 加载XML文档全文
执行当前XPath选取数据
图13.6 执行当前XPath选取数据

13.5 问题思考

问题提出:如何通过外部安装并引用xmldom模块及xpath模块?

问题思考:

安装并引用xmldom模块及xpath模块基本步骤如表13.3所述。

序号 步骤 指令或代码
1 安装xmldom模块 指令:npm install xmldom
2 安装xpath模块 指令:npm install xpath.js
3 引用xmldom模块 典型代码:
var dom = require("./xmldom").DOMParser;//获取类
var xmldom = new dom().parseFromString(data);//data为XML格式文本
4 引用xpath模块 典型代码:
var select = require("./xpath");//获取函数(xpath.js存放在xpath文件夹中)
5 执行XPath 典型代码:
Var nodes=select(xmldom,xpath);//获得节点集合

13.6 仿真实训

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?>
<table border="1" id="books">
    <tr style="background-color:greenyellow">
        <th id="s_id">学号</th>
        <th id="name">姓名</th>
        <th id="age">年龄</th>
    </tr>
    <tr>
        <td>5744885</td>
        <td>Max</td>
        <td>18</td>
    </tr>
    <tr>
        <td>2323885</td>
        <td>Jack</td>
        <td>17</td>
    </tr>
    <tr>
        <td>8888</td>
        <td>Roseg</td>
        <td>18</td>
    </tr>
    <tr>
        <td>8767885</td>
        <td>Json</td>
        <td>19</td>
    </tr>
    <tr>
        <td>64547565647689</td>
        <td>张三</td>
        <td>16</td>
    </tr>
    <tr>
        <td>45634748758478</td>
        <td>李四</td>
        <td>19</td>
    </tr>
    <tr>
        <td>675665647689</td>
        <td>王五</td>
        <td>18</td>
    </tr>
    <tr>
        <td>2342353758478</td>
        <td>刘六</td>
        <td>20</td>
    </tr>
</table>

现提出如下设计需求:

  1. 设计合适的用户接口,用户可自定输入XPath。
  2. 用户可查看XML文档原始数据。
  3. 用户可执行"XPath"语句并输出XML文档片断。