从一个范例看XML的应用

2024-08-29 23:48
文章标签 xml 应用 范例

本文主要是介绍从一个范例看XML的应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

数据库建立和数据访问

我们先看一下这个Web页面实现的功能:页面提供一些文本框供用户输入,包括书名、出版社、作者等信息,然后将这些信息发往服务器,服务器对数据库进行查询,然后返回查询结果。如果是通常的Asp.Net开发,完成这样的功能是很基本的要求,根本用不着我花时间写这些文字,但这里我们希望实现Ajax方式的效果,所以就需要解决引言中提出的问题。

如果你看过我的文章,那么应该知道我喜欢循序渐进的写作方式,这篇也是一样,我们先从数据库建立开始。由于数据库和数据访问并不是本文的重点,所以我只简单地描述一下步骤。在本地SQL Server或者直接在App_Data下新建一个数据库,起名叫SiteDB,然后建一个表Book,字段的设定如下:

随后填充一些范例数据,如果你想节约点时间,那么可以直接下载本文所附带的代码,在App_Data文件夹下包含有SiteDB数据库。

接下来我们在App_Code文件夹下添加一个SiteBLL.cs文件,本文用到的所有代码逻辑都包含在了SiteBLL类中,这么做显然是不妥的,但这里我们主要关注的是XML的应用,而非构架与设计,所以暂且就这个样子好了。很容易就能想到,我们要添加的第一个方法,会拥有下面这样的签名,它根据方法的参数查询数据库,然后以DataSet的形式返回结果:

private static DataSet SearchBook
    (string name, string author, string publisher, DateTime pubDate, decimal price)

如果要构建一个实际的查询,那么需要很大量的数据才能保证几乎每次搜索都能够获得到数据来提供演示,而实际上我们只添加了5条范例数据,所以让我们干脆将它们全部返回,而忽略这里的参数,但在实际当中,当然是根据这些参数来获得实际的返回数据:

private static DataSet SearchBook(string name, string author,
    string publisher, DateTime pubDate, decimal price) 
{
    string connString =
        WebConfigurationManager.ConnectionStrings["SiteDBConnection"].ConnectionString;
    string provider = 
        WebConfigurationManager.ConnectionStrings["SiteDBConnection"].ProviderName;

    DbProviderFactory factory = DbProviderFactories.GetFactory(provider);
    DbConnection conn = factory.CreateConnection();
    conn.ConnectionString = connString;

    DbDataAdapter adapter = factory.CreateDataAdapter();
    DbCommand selectCmd = conn.CreateCommand();
    selectCmd.CommandText = "Select * From Book";
    adapter.SelectCommand = selectCmd;

    DataSet ds = new DataSet("BookStore");
    adapter.Fill(ds, "Book");
    return ds;
}

这段代码没有什么好解释的,唯一值得注意的可能是我完全采用了面向接口(基类)的方式编写数据访问代码,这样将来如果更换为Oracle或者其他任何数据库,这里不需要更改一行代码,只需要修改下Web.Config就可以了。

XML应用 -- 单一字符串包含多种不同类型值

接下来我们对页面进行一下布局,如下所示:

控件的命名是自解释的,所以下面看代码应该不会遇到障碍,这里我就不再赘述了。需要注意的是页面上含有一个空的div标记,它用来承载我们的查询结果:

<div id="output"></div>

另外,“搜索”按钮是纯粹的HTML标记,不含有runat="server"属性,双击它,会在页面生成下面的javascript脚本段:

function btnSearch_onclick() {
    // ...
}

接下来我们要做的就是实现这个js方法,它的任务就是将文本框中输入的内容发往服务器。此时我们遇到了文章开头提出的问题,服务器期望的是5个参数,而且有字符串、数字、日期三种类型,而在客户端,我们只有一种类型 -- 字符串。因为javascript和C#显然用得不是一个类型系统,它们完全是两个领域。同时我们只发送一个参数,但要包含所有5个数值。对于现在以及和现在类似的情形,我将它统称为单一字符串包含多种不同类型的数值的情况,为了便于服务端(更宽泛点,叫程序)的处理,我们可以定义自己的XML。此处,我定义它的格式为:

<userInput>
    <name>书名</name>
    <author>作者</author>
    <publisher>出版社</publisher>
    <pubDate>出版日期</pubDate>
    <price>价格</price>
</userInput>

有了这个格式定义,实现btnSearch_onclick()就非常的容易了:

function btnSearch_onclick() {
    
    var name = <%="document.getElementById(\"" + txtName.ClientID + "\").value" %>;
    var author = <%="document.getElementById(\"" + txtAuthor.ClientID + "\").value" %>;
    var publisher = <%="document.getElementById(\"" + txtPublisher.ClientID + "\").value" %>;
    var pubDate = <%="document.getElementById(\"" + txtPubDate.ClientID + "\").value" %>;
    var price = <%="document.getElementById(\"" + txtPrice.ClientID + "\").value" %>;
    
    var inputXml = "<userInput>" + 
            "<name>" + name + "</name>" +
            "<author>" + author + "</author>" + 
            "<publisher>" + publisher + "</publisher>" + 
            "<pubDate>" + pubDate + "</pubDate>" + 
            "<price>" + price + "</price>" + 
            "</userInput>";
    
    var context = "Any data you want to pass !";
    
    ClientSearchBook(inputXml, context);
}

这段代码需要注意这样几点:

  1. 由于习惯问题,我给页面拖的是Asp.Net服务器控件,实际上,这里使用纯粹的Html Input标记就可以了,代码会更清爽一些,但是因为已经写好了,我偷懒了一下就没有改过去>_<、(但是使用服务器控件会有一个额外好处,就是可以使用验证控件,但是这里出于演示目的,我没有添加验证控件)。
  2. 这里的context和Asp.Net Ajax的两种基本开发模式 后面介绍的context作用一样,可以用来传递任何数据,这个值可以从调用成功或失败的回调方法中获得。
  3. ClientSearchBook()方法并没有实现,因为这篇文章我打算采用Asp.Net的脚本回调来实现,而不是用已经介绍过的Ajax Extension配合Web Service来实现,所以这个方法最后是由服务端生成的,这在后面会介绍到。现在只需知道它将inputXml发往服务端就可以了。

再次与Asp.Net Ajax的两种基本开发模式中介绍的第二种方式类似,我们实现onCompleted()和onFailed()这两个回调方法,它们将会在服务端生成的脚本代码中进行注册(后面会看到),当调用成功时调用onCompleted(),调用失败时调用onFailed()(这里我没有再演示context的使用了):

function onCompleted(result, context){
    output.innerHTML = result;
}

function onFailed(error, context){
    output.innerHTML = "Search Failed : " + error;
}

方法的实现只不过是将返回结果或者错误信息显示在页面的div标记中。

XML模式 -- 使用XSD校验客户端数据

我曾经听过这样一句Web编程的“谚语”――永远不要相信客户端发来的数据。意思就是说即便你添加了客户端的表单验证,仍然要在服务端对客户端发来的数据进行验证。在本文的例子中,我们接收的是一个XML字符串,那么如何对它进行验证呢?我们可以使用XML模式(XML Schema)来对它进行验证,XML模式文件的后缀名为xsd。对于XSD有这样一个很好的类比:就拿数据库的表定义来说,如果你定义的XML是表的列名,那么XSD就规定了列的类型(int还是bit,或者varchar)。

手工编写XML模式会很精细,但对于复杂的XML文档来说是很费力气的。在VS2008中,有一个内置功能,可以由XML文档推断出它的模式,尽管推断出的模式往往不够精准,但我们可以对推断出的模式进行一些修改,在大多数情况下就可以得到我们想要的模式。具体的做法是:创建一个符合预期输入的XML文件,用VS2008打开这个文件,然后在菜单栏选择“XML”-->“Create Schema”,再对这个生成的模式进行修改,最后保存在站点目录下,这里我将它保存为了userInputSchema.xsd:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" 
              elementFormDefault="qualified"
              xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="userInput">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string" />
                <xs:element name="author" type="xs:string" />
                <xs:element name="publisher" type="xs:string" />
                <xs:element name="pubDate" type="xs:date" />
                <xs:element name="price" type="xs:decimal" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

详细介绍XML模式需要花费很多的时间,所以这里我们只要知道它约束了name、author、publisher、pubDate、price这5个XML元素可以包含的数据类型就可以了。接下来我们就可以编写一个方法,针对XML文件进行验证了,在SiteBLL下再添加一个ValidateXmlSchema()方法:

private static bool ValidateXmlSchema(string xmlString, string xsdPath) {

    TextReader reader = new StringReader(xmlString);

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.Schemas.Add(null, xsdPath);
    XmlReader xmlReader = XmlReader.Create(reader, settings);

    try {
        while (xmlReader.Read()) { }
    } catch {
        return false;
    }
    return true;
}

这个方法的第一个参数是一个xml字符串,此处也就是客户端发来的数据;第二个参数是XML模式的文件路径。在方法内部使用了一个XmlReader遍历了Xml文档,由于对XmlReader设置了模式,所以在遍历时会对每一个节点进行验证,当发现不符合模式要求的节点值时便会抛出异常,如果我们捕获到异常,就返回false。上面有一个很常见的应用这里顺便说一下,可以注册XmlReaderSettings对象的ValidationEventHandler事件,注册这个事件后发现不符合模式的节点时可以交给事件处理程序处理,而不会抛出异常。这个事件的参数包含了错误的详细信息,例如哪个节点的验证失败,还可以区分是一个“警告”还是一个“错误”。

XSLT样式表 -- 从XML 到 XHTML

OK,处理客户端的处理现在已经告一段落了,让我们再次看一看服务端SearchBook()方法的签名:

private static DataSet SearchBook
    (string name, string author, string publisher, DateTime pubDate, decimal price)

我们看到它返回的是一个DataSet,而在客户端,我们期望接收的是一个字符串,虽然我们可以在服务端遍历DataSet中的表,然后对其字段值进行处理,比如嵌入一些HTML代码,然后将处理好的HTML代码返回。但是有一种更加“fashion”的做法,就是使用XSLT进行转换。为了进行转换,我们首先要获得DataSet的XML形式的表现,这可以方便地通过在DataSet对象上调用GetXml()方法来获得。随后,我们需要以编程的方式对这个XML进行XSLT转换,将其转换为预期的XHTML。

开始之前,我们需要知道我们在DataSet上调用GetXml()方法获得的结果,因为我们将DataSet命名为了BookStore,将表命名为了Book,所以XML应该为类似下面的形式:

<BookStore>
    <Book>
        <Id>1</Id>
        <Name>SQL Server 2005宝典</Name>
        <Author>Paul Nielsen</Author>
        <Publisher>人民邮电出版社</Publisher>
        <PubDate>2006-10-01T00:00:00+08:00</PubDate>
        <Price>65.50</Price>
    </Book>
    <Book>
        ...
    </Book>
</BookStore>

接下来我们要编写一个XSLT样式表文件,对类似上面的数据进行转换,将它们转成标准的表格:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"    
                     xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
                     exclude-result-prefixes="msxsl">
    <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
         <table class="mainTable">
             <tr style="background:#f5f5f5;">
                 <th style="width:20%;">书名</th>
                 <th style="width:20%;">作者</th>
                 <th style="width:20%;">出版社</th>
                 <th style="width:20%;">出版日期</th>
                 <th style="width:20%;">定价</th>
             </tr>
             <xsl:for-each select="/BookStore/Book">
                 <xsl:element name="tr">
                     <xsl:element name="td">
                         <xsl:value-of select="Name" />
                     </xsl:element>
                     <xsl:element name="td">
                         <xsl:value-of select="Author" />
                     </xsl:element>
                     <xsl:element name="td">
                         <xsl:value-of select="Publisher" />
                     </xsl:element>
                     <xsl:element name="td">
                         <xsl:value-of 
                     select="msxsl:format-date(PubDate, 'yyyy-M-dd')" />
                     </xsl:element>
                     <xsl:element name="td">
                         <xsl:value-of select="Price" />
                     </xsl:element>
                 </xsl:element>             
             </xsl:for-each>
         </table> 
    </xsl:template>
</xsl:stylesheet>

与XML模式类似,解释XSLT需要很多的篇幅,本文不打算详细对它进行解释。现在只要知道它可以将一个原始XML转换成各种格式的目标文档,其中之一是XHTML就可了。上面的XSLT将DataSet输出的XML转换成了一个HTML的Table标记。

有了这个XSLT样式表,接下来我们就可以在SiteBLL中再添加一个方法:

// 使用XSLT将XML转换为XHTML
private static string ConvertToXhtml(string xml, string xslPath) {
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    XslCompiledTransform transform = new XslCompiledTransform();
    transform.Load(xslPath);
    TextWriter writer = new StringWriter();
    transform.Transform(doc, null, writer);

    return writer.ToString();
}

ConvertToXhtml()只是进行XSLT转换的一个最简单的代码,但足以满足本文中我们的需求。实际上,我们在进行XSLT转换的时候,还可以向XSLT样式表传递服务器端的对象和参数,以后有时间再为大家介绍。

SearchBook()重载方法

我们很快又回到了曾在 Asp.Net Ajax的两种基本开发模式 中讨论过的基本模式,服务端接受一个字符串类型,返回一个字符串类型。只不过这次接受的字符串类型为XML格式,而返回的是经过XSLT格式化成XHTML的DataSet。为了便于使用,我们将所有的从XML中获得值、XML 模式验证、XSLT转换包装在一个SearchBook()的重载方法中:

public static string SearchBook(string xmlString, string xsdPath, string xslPath) {

    XmlDocument doc = new XmlDocument();

    if (ValidateXmlSchema(xmlString, xsdPath)) {

        doc.LoadXml(xmlString);
        XmlNode root = doc.DocumentElement;

        string name = root.SelectSingleNode("name").InnerText;
        string author = root.SelectSingleNode("author").InnerText;
        string publisher = root.SelectSingleNode("publisher").InnerText;
        DateTime pubDate =
            Convert.ToDateTime(root.SelectSingleNode("pubDate").InnerText);
        decimal price =
            Convert.ToDecimal(root.SelectSingleNode("price").InnerText);

        string xml = SearchBook(name, author, publisher, pubDate, price).GetXml();
        string xhtml = ConvertToXhtml(xml, xslPath);
        return xhtml;
    }

    return "Your input is invalid !";
}

这段代码非常简单,没有什么特别之处。需要注意的是:当模式验证失败的时候,返回的是一个字符串“Your input is invalid !”。这里的信息显然太少了,如同我在上面所说,你可以在验证时,注册XmlReaderSettings对象的ValidationEventHandler事件,然后在事件的处理方法中获得更详细的信息(哪个节点验证失败了,什么原因)。

启用Asp.Net脚本回调

我们终于又回到了页面的设置当中,但这次不是布置页面控件,而是启用Asp.Net的脚本回调功能。我们要做的第一步,就是让Web页面实现ICallbackEventHandler接口,它的实现如下:

private string userInput;

void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) {
    userInput = eventArgument;
}

string ICallbackEventHandler.GetCallbackResult() {
    string xsdPath = Server.MapPath("userInputSchema.xsd");
    string xslPath = Server.MapPath("userInputXsl.xslt");

    return SiteBLL.SearchBook(userInput, xsdPath, xslPath); 
}

RaiseCallBackEvent()方法接收一个eventArgument字符串,这个字符串即为客户端发往服务端的值,也就是我们在btnSearch_onclick()构建的inputXml字符串,我们将它保存在一个私有变量中。GetCallbackResult()方法使用这个私有变量,并调用了我们上一小节创建的SearchBook()方法,返回了XHTML字符串。

至此,还有一个问题没有解决:我们没有将客户端onComplted()和onFailed()与Asp.Net的脚本回调关联起来,除此以外,应该记得在btnSearch_onclick()方法中调用了一个“奇怪”的客户端javascript方法ClientSearchBook(),而它却并没有在页面中实现。实际上,这个方法是自动生成的,现在改写页面的Page_Load()方法:

protected void Page_Load(object sender, EventArgs e) {

    if (!Request.Browser.SupportsCallback)
        throw new ApplicationException("Browser doesn't support callbacks !");

    string methodBody = Page.ClientScript.GetCallbackEventReference
        (this"arg", "onCompleted", "context", "onFailed"false);

    string method = @"function ClientSearchBook(arg, context){" + methodBody + ";}";

    Page.ClientScript.RegisterClientScriptBlock
        (this.GetType(), " ClientSearchBook", method, true);
}

GetCallbackEventReference()方法关联了客户端的onCompleted和onFailed方法,分别用于成功和失败时的回调。它的第一个参数是实现了ICallbackEventHandler的控件,此处就是当前的Page页面了;第二个参数是客户端发往服务端的数据;第三个参数是方法成功时的回调方法;第四个参数是我们的老熟人context,它被用于回调的onComplted()和onFailed()方法中;第五个参数是方法失败时的回调方法;最后一个说明是否异步调用。

GetCallbackEventReference()方法返回了一段javascript脚本,这段脚本只是一个javascript方法的方法体。 所以,我们接着构建了一个包含完整方法的字符串。最后我们将这个方法注册到了页面上。所以当你打开页面时,会发现页面中已经生成了btnSearch_onclick()中所调用的这个ClientSearchBook()。

<script type="text/javascript">
//<![CDATA[
function ClientSearchBook(arg, context){
    WebForm_DoCallback('__Page',arg,onCompleted,context,onFailed,false);
}
//]]>
</script>

如果你对这段代码中的WebForm_DoCallback()方法感到奇怪,不知道它位于何处,那么你可以找到这段代码:

<script src="/WebSite/WebResource.axd?d=gTLcCoR1D13V4dcBYSU_JA2&amp;t=633432946018437500" type="text/javascript"></script>

将其中的/WebSite/WebResource.axd?d=gTLcCoR1D13V4dcBYSU_JA2&amp;t=633432946018437500 复制到浏览器的合适位置,然后会下载到一个WebResource.axd文件,用文本编辑器打开这个文件,可以看到许多的javascript代码,其中就包括WebForm_DoCallback()方法,这些便是由Microsoft所实现的方法回调的底层代码了。

效果预览

现在,我们可以打开页面浏览一下效果了,我们先输入一个不正确的日期格式,然后点击搜索,会看到下面的结果:

然后我们将日期修改正确,再次进行输入,可以看到下面的结果:

总结

这篇文章为大家演示了一个XML的综合应用:使用字符串传递自定义数值、使用XML模式验证XML的有效性、使用XSLT将XML转换为XHTML标记,以及使用Asp.Net的脚本回调功能实现Ajax的效果。

通过这篇文章,可以看到XML的广泛应用,但是也发现了实现这样一个简单的功能却需要做如此繁杂的工作。所以,我个人觉得如果想要一些更巧妙的设计、更优良的性能,那么可以采用这样的方式。但是如果要追求更高的开发效率,我想一个UpdatePanel再加一个GridView就足以完成上面的功能了吧。

感谢阅读,希望这篇文章能给你带来帮助!

这篇关于从一个范例看XML的应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1119202

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

AI行业应用(不定期更新)

ChatPDF 可以让你上传一个 PDF 文件,然后针对这个 PDF 进行小结和提问。你可以把各种各样你要研究的分析报告交给它,快速获取到想要知道的信息。https://www.chatpdf.com/

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

基于 YOLOv5 的积水检测系统:打造高效智能的智慧城市应用

在城市发展中,积水问题日益严重,特别是在大雨过后,积水往往会影响交通甚至威胁人们的安全。通过现代计算机视觉技术,我们能够智能化地检测和识别积水区域,减少潜在危险。本文将介绍如何使用 YOLOv5 和 PyQt5 搭建一个积水检测系统,结合深度学习和直观的图形界面,为用户提供高效的解决方案。 源码地址: PyQt5+YoloV5 实现积水检测系统 预览: 项目背景