给定如下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> <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>
现提出如下设计需求:
测试用户期望功能如下:
用户测试功能用例建模如图16.1所示。
图16.1 功能用例建模
功能模块中的核心业务是:输入新图书信息并保存。
核心业务流程描述如下:
核心业务数据流建模如图16.2所示。
图16.2 核心业务数据流建模
基于三层架构视角的文件包及文件设计如表16.1所述。
序号 | 包设计 | 文件设计 |
---|---|---|
1 | 设计表示层包(包名:UI),抽象表示层文件容器。 | (1) 设计界面主页文件main.js。 (2) 主页引入jquery框架(jquery.js),辅助表示层处理。 |
2 | 设计业务逻辑层包(包名:server),抽象业务层文件容器。 | (1) 设计通用业务逻辑服务文件server.js。 (2) 设计当前模块专用业务逻辑服务文件server_16.js。 (3) 引入xmldom模块(xmldom.js,外部安装获得)。 (4) 引入xpath模块(xpath.js,外部安装获得)。 |
3 | 设计数据层包(包名:data),抽象数据层文件容器。 | (1) 设计XML数据存储文件books.xml。 (2) 设计XML数据备份文件books_bak.xml |
文件包设计及文件关系如图16.3所示。
图16.3 文件包设计及文件关系
为了减轻前端设计压力,可对现有XML文档进行创新设计,考虑到添加数据必须要有数据输入接口,而输入接口的数目与XML文档中数据条目数量有关,具体到以上文档,实际输入接口数目与<td>标记数量应该一致。
基于以上分析,可事先在以上给定XML文档中增加一行(即增加一个特定<tr>标记),该特定<tr>标记可事先嵌入HTML代码,目的是让应用程序读取这个特定<tr>即可得到输入界面,这样可避免前端设计人员再去设计数据输入界面。
需增加的特定<tr>标记设计如下:
<tr id="add_update_XML"> <td> <input type="text"/> </td> <td> <input type="text"/> </td> <td> <input type="text"/> </td> </tr>
具体创新改进后的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>
应用程序读取id="add_update_XML"的 <tr>即可得到用户输入界面。
对于轻量级XML文档来说,可以将整个文档读取为字符串,由于文档最后必定是结束标记</table>;因此,只要将该字符串替换成新数据对应的XML格式串,即可实现节点添加。
假定整个XML文档对应的字符串为XMLtext,新增加的数据对应的字符串为newDatatext,则调用以下替换代码即可实现添加新节点。
XMLtext.replace("</table>",newDatatext)
文件架构如图16.4所示。
图16.4 文件架构
文件功能说明见表16.4所述。
序号 | 文件(夹)名 | 作用 |
---|---|---|
1 | books.xml | 用于测试的XML基础数据 |
2 | Server.js | NodeJS后台服务主程序 |
3 | Server_16.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 | books_bak.xml | books.xml的备份数据 |
主页main.html实现代码如下:
<!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="button" value="添加图书" onclick="start_add_TagData()" /> <input type="button" value="查看全部图书" onclick="view_All()" /> <input type="button" value="恢复数据为初始状态" onclick="restore_TagData()" /> <hr /> <div id="outer_div"> </div> <hr /> <input id="post_but" type="button" value="提交新增图书" disabled="disabled" onclick="post_TagData()" /> </body> </html> <script type="text/javascript" src="jquery-1.11.1.js"></script> <script type="text/javascript"> //基于XPath的标记数据添加 function start_add_TagData() { var xhttp = new XMLHttpRequest(); xhttp.open("post", "http://localhost:1017/startAdd_16", true); xhttp.onreadystatechange = function () { if (xhttp.status == 200 && xhttp.readyState == 4) { $("#outer_div").html("<table border='1'>" + xhttp.responseText + "</table>"); $("#post_but").attr("disabled",false); } } xhttp.send(); } //提交新增图书 function post_TagData() { var inputs = $("#add_update_XML").find("input"); var isEmpty = false; for (var k = 0; k < inputs.length; k++) { if ($(inputs[k]).val() == "") { isEmpty = true; } } if (isEmpty) { alert("各项信息不能为空!"); } else{ var new_tr = "<tr>\n"; for (var k = 0; k < inputs.length; k++) { new_tr += "<td>" + $(inputs[k]).val() + "</td>\n"; } new_tr += "</tr>"; //将新行提交到后台 var xhttp = new XMLHttpRequest(); xhttp.open("post", "http://localhost:1017/AddXMLDataRow_16?param="+encodeURIComponent(new_tr), true); xhttp.onreadystatechange = function () { if (xhttp.status == 200 && xhttp.readyState == 4) { alert(xhttp.responseText); } } xhttp.send(); } } //查看全部图书 function view_All() { var xhttp = new XMLHttpRequest(); xhttp.open("post", "http://localhost:1017/viewAll_16", true); xhttp.onreadystatechange = function () { if (xhttp.status == 200 && xhttp.readyState == 4) { $("#outer_div").html(xhttp.responseText); $("#add_update_XML").hide(); } } xhttp.send(); } //恢复数据 function restore_TagData() { if (confirm("确定要将数据恢复到最初状态?")) { var xhttp = new XMLHttpRequest(); xhttp.open("post", "http://localhost:1017/restoreData_16", true); xhttp.onreadystatechange = function () { if (xhttp.status == 200 && xhttp.readyState == 4) { alert(xhttp.responseText); } } xhttp.send(); } } </script>
服务主程序server.js实现代码如下:
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("./16/server/server_16.js")(request, response, req_head, Q_obj, fs_obj); //%%%%%%%%%%%%%------------------end your code }); server.listen(1017); console.log("Server is running at port 1017...");
子模块程序server_16.实现代码如下:
function server_16(request, response, req_head, Q_obj, fs_obj) { //%%%%%%%%%%%%%----------------begin your code if (req_head == "/startAdd_16") { //添加前准备 var b_xpath = "/table/tr[position()<3]"; var select = require("./xpath");//获取查询函数 var dom = require("./xmldom").DOMParser;//获取DOMParser类 fs_obj.readFile("./16/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()); } }); return; } if (req_head == "/AddXMLDataRow_16") { //添加数据(尾部字符串替换) var b_new_tr = Q_obj["param"]; fs_obj.readFile("./16/data/books.xml", "utf-8", function (err, data) { if (err) { response.end(err); } else { var new_xml = data.replace("</table>",b_new_tr+"\n</table>") ; fs_obj.writeFile("./16/data/books.xml", new_xml, function (err) { if (err) { response.end(err); } else { response.end("成功添加数据"); } }); } }); return; } if (req_head == "/viewAll_16") { //查看全部图书 fs_obj.readFile("./16/data/books.xml", "utf-8", function (err, data) { if (err) { response.end(err); } else { response.end(data); } }); return; } if (req_head == "/restoreData_16") { fs_obj.readFile("./16/data/books_bak.xml", "utf-8", function (err, data) { if (err) { response.end(err.toString()); } else { fs_obj.writeFile("./16/data/books.xml", data, function (err) { if (err) { response.end(err.toString()); } else { response.end("成功恢复数据"); } }); } }); return; } //%%%%%%%%%%%%%------------------end your code } module.exports=server_16
数据添加实现效果如图16.5所示。
图16.5 数据添加实现效果
问题提出:添加XML节点的主要技术要点是什么?
问题思考:
给定如下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>
现提出如下设计需求: