第18章 XML格式数据删除

18.1 设计需求

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?>
<table border="1" id="books">
    <tr bgcolor="green">
        <th id="ISBN">书号</th>
        <th id="title">书名</th>
        <th id="price">价格</th>
    </tr>
    <tr id="add_update_XML">
        <td>
            <input type="text"/>
        </td>
        <td>
            <input type="text"/>
        </td>
        <td>
            <input type="text"/>
        </td>
    </tr>
    <tr>
        <td>5744885</td>
        <td>Java programming</td>
        <td>80</td>
    </tr>
    <tr>
        <td>2323885</td>
        <td>C# programming</td>
        <td>87</td>
    </tr>
    <tr>
        <td>8888</td>
        <td>Python programming</td>
        <td>68</td>
    </tr>
    <tr>
        <td>8767885</td>
        <td>NodeJS</td>
        <td>90</td>
    </tr>
    <tr>
        <td>r64547565647689</td>
        <td>C++ programming</td>
        <td>68</td>
    </tr>
    <tr>
        <td>45634748758478</td>
        <td>MongoDB</td>
        <td>90</td>
    </tr>
    <tr>
        <td>675665647689</td>
        <td>operation system</td>
        <td>68</td>
    </tr>
    <tr>
        <td>2342353758478</td>
        <td>XML技术</td>
        <td>90</td>
    </tr>
</table>

现提出如下设计需求:

  1. 设计nodeJS后台服务,该服务可在后台实现"删除图书信息"业务。
  2. 后台需基于DOM模型实现XML节点删除。
  3. 基于Ajax技术及三层架构实现"删除图书"业务。

18.2 设计分析

18.2.1 功能分析

测试用户期望功能如下:

  1. 测试用户可查看测试XML文档全部数据。
  2. 测试用户可删除特定图书信息。
  3. 测试用户可将图书信息恢复到初始状态。
功能用例建模
图18.1 功能用例建模

18.2.2 核心业务数据流分析

功能模块中的核心业务是:删除特定图书信息并保存XML源文件。

核心业务流程描述如下:

  1. 用户使用模糊查询方法查询到特定图书信息。
  2. 用户在模糊查询结果选取特定图书并确认删除。
  3. 将执行删除操作后的内存数据改写XML源文件。
  4. 查看删除后的XML源文件格式化数据。
核心业务数据流
图18.2 核心业务数据流

18.2.3 文件包及文件设计分析

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

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

18.2.4 XML文档改进说明

基于XML格式数据的操作往往集成在一起,因此,XML文档的设计与改进应与前面章节一样。

具体创新改进后的XML文档如下:

<?xml version="1.0" encoding="utf-8"?>
<table border="1" id="books">
    <tr bgcolor="green">
        <th id="ISBN">书号</th>
        <th id="title">书名</th>
        <th id="price">价格</th>
    </tr>
    </tr><tr id="old_data">
    </tr>
    <tr id="add_update_XML">
        <td>
            <input class="edit_data" style="color:blue" type="text"/>
        </td>
        <td>
            <input class="edit_data" style="color:blue" type="text"/>
        </td>
        <td>
            <input class="edit_data" style="color:blue" type="text"/>
        </td>
        <td>
            <input type="button" value="提交新数据" onclick="post_new_XMLdata()"/>
            <input type="button" value="取消" onclick="$('tr').hide()"/>
        </td>
    </tr>
    <tr>
        <td>5744885</td>
        <td>Java programming</td>
        <td>80</td>
    </tr>
    <tr>
        <td>2323885</td>
        <td>C# programming</td>
        <td>87</td>
    </tr>
    <tr>
        <td>8888</td>
        <td>Python programming</td>
        <td>68</td>
    </tr>
    <tr>
        <td>8767885</td>
        <td>NodeJS</td>
        <td>90</td>
    </tr>
    <tr>
        <td>r64547565647689</td>
        <td>C++ programming</td>
        <td>68</td>
    </tr>
    <tr>
        <td>45634748758478</td>
        <td>MongoDB</td>
        <td>90</td>
    </tr>
    <tr>
        <td>675665647689</td>
        <td>operation system</td>
        <td>68</td>
    </tr>
    <tr>
        <td>2342353758478</td>
        <td>XML技术</td>
        <td>90</td>
    </tr>
</table>

由于id="add_update_XML"的<tr>已事先设定在XML文档中,可从后台读取到前端并在网页中可视化,该<tr>标记中所定义的用户输入接口,既可用于添加数据,也可用于修改数据。

18.3 文件架构

文件架构
图18.4 文件架构

文件功能说明见表18.2所述。

表18.2 文件及文件夹功能说明
序号 文件(夹)名 作用
1 books.xml 用于测试的XML基础数据
2 Server.js NodeJS后台服务通用逻辑
3 Server_17.js 服务当前应用的后台核心代码
4 jquery-1.11.1.js 加载JQuery函数库
5 main.html 用户界面主页
6 Data 存放XML数据文件的文件夹
7 server 存放业务逻辑层文件的文件夹
8 UI 存放表示层文件的文件夹
9 xmldom 安装xmldom模块产生的文件夹
10 xpath 安装xpath模块产生的文件夹
11 Z_doc 辅助文档,不参与代码运行

18.4 代码实现

18.4.1 主页实现

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h2>标记数据删除测试</h2>
    <hr />
    请输入书名<input type="text" id="title_input" />
    <input type="button" value="模糊查询图书" onclick="select_TagData()" />
    <hr />
    <input type="button" value="恢复数据为初始状态" onclick="restore_TagData()" />
    <hr />
    <div id="outer_div">
    </div>
</body>
</html>

<script type="text/javascript" src="jquery-1.11.1.js"></script>

<script type="text/javascript">
//基于XPath的标记数据模糊查询
function select_TagData() {
    var book_name = $("#title_input").val();
    var xhttp = new XMLHttpRequest();
    xhttp.open("post", "http://localhost:1017/mhFind_18?param=" + encodeURIComponent(book_name), true);
    xhttp.onreadystatechange = function () {
        if (xhttp.status == 200 && xhttp.readyState == 4) {
            $("#outer_div").html("<table border='1'>" + xhttp.responseText + "</table>");
            $("#old_data").hide();
            $("#add_update_XML").hide();
            var trs = $("tr");
            for (var k = 3; k < trs.length; k++) {
                trs[k].innerHTML = trs[k].innerHTML + '<td><input type="button" value="删除" onclick="exe_delete(this)" /></td>';
            }
        }
    }
    xhttp.send();
}

//执行删除
function exe_delete(obj) {
    //获取关键字
    var cur_tr = $(obj).parent().parent();
    var key_isbn = cur_tr.find("td")[0].innerHTML;
    if (confirm("确认要删除书号为" + key_isbn+"的图书信息吗?")) {
        //提交关键字到后台
        var xhttp = new XMLHttpRequest();
        xhttp.open("post", "http://localhost:1017/postDelete_18?param1=" + encodeURIComponent(key_isbn), true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                alert(xhttp.responseText);
            }
        }
        xhttp.send();
    }
}

//恢复数据
function restore_TagData() {
    if (confirm("确定要将数据恢复到最初状态?")) {
        var xhttp = new XMLHttpRequest();
        xhttp.open("post", "http://localhost:1017/restoreData_18", true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                alert(xhttp.responseText);
            }
        }
        xhttp.send();
    }
}
</script>

18.4.2 后台服务通用逻辑

// 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("./18/server/server_18.js")(request, response, req_head, Q_obj, fs_obj);
    
    //%%%%%%%%%%%%%-------------end your code
});

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

18.4.3 后台服务专用逻辑

function server_18(request, response, req_head, Q_obj, fs_obj) {
    //%%%%%%%%%%%%%------------begin your code
    
    if (req_head == "/mhFind_18") {
        //模糊查询
        var b_title = Q_obj["param"];
        var b_xpath = "/table/tr[position()<4] | /table/tr[contains(td[2],'" + b_title + "')]";
        var select = require("./xpath");//获取查询函数
        var dom = require("./xmldom").DOMParser;//获取DOMParser类
        
        fs_obj.readFile("./18/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);
                if (nodes.length < 4) { response.end("未找到相关数据!"); }
                else { response.end(nodes.toString()); }
            }
        });
        return;
    }
    
    //以下删除XML文档片断
    if (req_head == "/postDelete_18") {
        var old_isbn = Q_obj["param1"];
        fs_obj.readFile("./18/data/books.xml", "utf-8", function (err, data) {
            if (err) { response.end(err); }
            else {
                var dom = require("./xmldom").DOMParser;
                var XMLdom = new dom().parseFromString(data);
                var tr_doms = XMLdom.getElementsByTagName("tr");
                
                for (var k = 2; k < tr_doms.length; k++) {
                    var temp_isbn = tr_doms[k].getElementsByTagName("td")[0].textContent;
                    if (temp_isbn == old_isbn) {
                        tr_doms[k].parentNode.removeChild(tr_doms[k]);
                        var new_XMLtext = '<?xml version="1.0" encoding="utf-8"?>\n';
                        new_XMLtext += '<table border="1" id="books">\n';
                        new_XMLtext += tr_doms.toString() + "\n</table>";
                        
                        fs_obj.writeFile("./18/data/books.xml", new_XMLtext, function (err) {
                            if (err) { response.end(err); }
                            else { response.end("成功删除数据"); }
                        });
                        break;
                    }
                }
            }
        });
        return;
    }
    
    if (req_head == "/restoreData_18") {
        fs_obj.readFile("./18/data/books_bak.xml", "utf-8", function (err, data) {
            if (err) { response.end(err.toString()); }
            else {
                fs_obj.writeFile("./18/data/books.xml", data, function (err) {
                    if (err) { response.end(err.toString()); }
                    else { response.end("成功恢复数据"); }
                });
            }
        });
        return;
    }
    
    //%%%%%%%%%%%%%-------------end your code
}

module.exports=server_18

18.4.4 实现效果

数据删除前模糊查询如图18.2所示。

数据删除前查询效果
图18.2 数据删除前查询效果

数据删除进行中如图18.3所示。

数据删除进行中实现效果
图18.3 数据删除进行中实现效果

18.5 问题思考

问题提出:删除XML节点的主要技术要点是什么?

问题思考:

  1. 读取特定<tr>标记并在网页中可视化。
  2. 在可视化后的用户接口中选定即将删除的数据。
  3. 删除前,弹出安全提示,避免误删除。
  4. 将关键字提交到后台。
  5. 后台程序将源XML文档转化成文档对象。
  6. 从文档对象中找出所有<tr>标记并生行对象集合。
  7. 后端根据前端传递过来的搜索关键字定位到要删除的行对象。
  8. 在行对象集合中删除已定位的行对象。
  9. 将新的行对象转化为字符串并写入源文件。

18.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 id="add_update_XML">
        <td>
            <input type="text"/>
        </td>
        <td>
            <input type="text"/>
        </td>
        <td>
            <input type="text"/>
        </td>
    </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. 设计nodeJS后台服务,该服务可在后台实现"删除图书"业务。
  2. 基于Ajax技术及三层架构实现"删除图书"业务。
  3. 后台基于DOM实现删除XML节点。
  4. 仿真案例功能实现基于以上设计需求的业务。