活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

探索NET框架下XSLT技术实现高效XML数据转换与处理的实战指南

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-1 13:50:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1. 引言:XSLT与XML数据处理的重要性

在当今数据驱动的世界中,XML(可扩展标记语言)作为一种通用的数据交换格式,在各种企业应用、Web服务和系统集成中扮演着至关重要的角色。然而,原始的XML数据往往需要转换为其他格式以满足不同的业务需求,这就需要强大而灵活的转换工具。

XSLT(可扩展样式表语言转换)正是为此而生的技术,它专门用于将XML文档转换为其他格式,如HTML、PDF、纯文本或其他XML结构。在.NET框架中,XSLT得到了全面的支持,为开发者提供了强大的工具集来处理复杂的XML转换任务。

本文将深入探讨如何在.NET框架下利用XSLT技术实现高效的XML数据转换与处理,通过理论讲解和丰富的实战案例,帮助开发者掌握这一重要技术。

2. XSLT基础概念

2.1 什么是XSLT

XSLT(Extensible Stylesheet Language Transformations)是一种用于将XML文档转换为其他XML文档或其他格式(如HTML、纯文本等)的语言。它是XSL(Extensible Stylesheet Language)家族的一部分,专门负责文档的转换部分。

XSLT使用XPath(XML Path Language)来导航和查询XML文档中的元素和属性,通过模板规则定义转换逻辑,实现了数据与表示的分离。

2.2 XSLT工作原理

XSLT转换过程遵循以下基本步骤:

1. 解析源XML文档:XSLT处理器读取并解析源XML文档,构建内存中的树形结构。
2. 加载XSLT样式表:处理器加载定义转换规则的XSLT样式表。
3. 应用模板规则:处理器按照XSLT中的模板规则,对源XML树的节点进行匹配和转换。
4. 生成结果文档:根据转换规则生成目标格式的输出文档。

2.3 XSLT基本语法

XSLT文档本身也是XML文档,使用特定的元素和属性来定义转换规则。以下是一些基本的XSLT元素:

• <xsl:stylesheet>或<xsl:transform>:XSLT样式表的根元素
• <xsl:template>:定义模板规则,指定如何处理匹配的节点
• <xsl:value-of>:提取并输出选定节点的值
• <xsl:for-each>:循环处理节点集合
• <xsl:if>:条件判断
• <xsl:choose>、<xsl:when>、<xsl:otherwise>:多条件选择
• <xsl:apply-templates>:应用其他模板规则

下面是一个简单的XSLT示例,将XML数据转换为HTML表格:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:template match="/">
  4.         <html>
  5.             <body>
  6.                 <h2>员工信息</h2>
  7.                 <table border="1">
  8.                     <tr bgcolor="#9acd32">
  9.                         <th>姓名</th>
  10.                         <th>职位</th>
  11.                         <th>部门</th>
  12.                     </tr>
  13.                     <xsl:for-each select="company/employee">
  14.                         <tr>
  15.                             <td><xsl:value-of select="name"/></td>
  16.                             <td><xsl:value-of select="position"/></td>
  17.                             <td><xsl:value-of select="department"/></td>
  18.                         </tr>
  19.                     </xsl:for-each>
  20.                 </table>
  21.             </body>
  22.         </html>
  23.     </xsl:template>
  24. </xsl:stylesheet>
复制代码

3. .NET框架中的XSLT支持

3.1 System.Xml.Xsl命名空间

.NET框架通过System.Xml.Xsl命名空间提供了一系列类来支持XSLT转换。主要类包括:

• XslCompiledTransform:.NET Framework 2.0及以后版本中的主要XSLT处理器,提供了高性能的XSLT转换功能。
• XslTransform:.NET Framework 1.x版本中的XSLT处理器,已过时但为了向后兼容仍然保留。
• XsltArgumentList:用于向XSLT转换传递参数和扩展对象。
• XsltSettings:用于配置XSLT转换的安全设置。

3.2 XslCompiledTransform类

XslCompiledTransform是.NET中进行XSLT转换的核心类,它提供了以下主要方法:

• Load():加载XSLT样式表,可以从文件、URI、XmlReader等多种来源加载。
• Transform():执行转换,可以接受多种输入和输出形式。

下面是一个使用XslCompiledTransform进行基本转换的示例:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.Xml.XPath;
  5. using System.IO;
  6. public class XsltTransformation
  7. {
  8.     public static void Main()
  9.     {
  10.         // 创建XslCompiledTransform对象
  11.         XslCompiledTransform xslt = new XslCompiledTransform();
  12.         
  13.         // 加载XSLT样式表
  14.         xslt.Load("transform.xslt");
  15.         
  16.         // 执行转换
  17.         xslt.Transform("input.xml", "output.html");
  18.         
  19.         Console.WriteLine("转换完成!");
  20.     }
  21. }
复制代码

3.3 XsltArgumentList的使用

在实际应用中,我们经常需要向XSLT转换传递外部参数或扩展对象。XsltArgumentList类就是为此设计的。

以下示例展示了如何向XSLT传递参数:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.Xml.XPath;
  5. using System.IO;
  6. public class XsltWithParameters
  7. {
  8.     public static void Main()
  9.     {
  10.         // 创建XslCompiledTransform对象
  11.         XslCompiledTransform xslt = new XslCompiledTransform();
  12.         
  13.         // 加载XSLT样式表
  14.         xslt.Load("transform.xslt");
  15.         
  16.         // 创建XsltArgumentList并添加参数
  17.         XsltArgumentList argsList = new XsltArgumentList();
  18.         argsList.AddParam("companyName", "", "ABC科技有限公司");
  19.         argsList.AddParam("currentDate", "", DateTime.Now.ToString("yyyy-MM-dd"));
  20.         
  21.         // 执行转换,传递参数
  22.         using (XmlReader input = XmlReader.Create("input.xml"))
  23.         using (XmlWriter output = XmlWriter.Create("output.html"))
  24.         {
  25.             xslt.Transform(input, argsList, output);
  26.         }
  27.         
  28.         Console.WriteLine("带参数的转换完成!");
  29.     }
  30. }
复制代码

对应的XSLT文件中,可以通过<xsl:param>元素接收这些参数:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:param name="companyName"/>
  4.     <xsl:param name="currentDate"/>
  5.    
  6.     <xsl:template match="/">
  7.         <html>
  8.             <body>
  9.                 <h1><xsl:value-of select="$companyName"/>员工信息</h1>
  10.                 <p>生成日期:<xsl:value-of select="$currentDate"/></p>
  11.                 <!-- 其他转换内容 -->
  12.             </body>
  13.         </html>
  14.     </xsl:template>
  15. </xsl:stylesheet>
复制代码

4. 基本XSLT转换实现

4.1 简单XML到HTML的转换

让我们通过一个完整的示例,展示如何使用.NET中的XSLT技术将XML数据转换为HTML页面。

首先,假设我们有以下员工信息的XML文件(employees.xml):
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <company>
  3.     <employee id="1">
  4.         <name>张三</name>
  5.         <position>软件工程师</position>
  6.         <department>研发部</department>
  7.         <email>zhangsan@example.com</email>
  8.         <phone>13800138000</phone>
  9.     </employee>
  10.     <employee id="2">
  11.         <name>李四</name>
  12.         <position>产品经理</position>
  13.         <department>产品部</department>
  14.         <email>lisi@example.com</email>
  15.         <phone>13900139000</phone>
  16.     </employee>
  17.     <employee id="3">
  18.         <name>王五</name>
  19.         <position>UI设计师</position>
  20.         <department>设计部</department>
  21.         <email>wangwu@example.com</email>
  22.         <phone>13700137000</phone>
  23.     </employee>
  24. </company>
复制代码

接下来,我们创建一个XSLT样式表(employees_to_html.xslt),将上述XML转换为HTML表格:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:output method="html" indent="yes"/>
  4.    
  5.     <xsl:template match="/">
  6.         <html>
  7.             <head>
  8.                 <title>员工信息表</title>
  9.                 <style>
  10.                     table { border-collapse: collapse; width: 100%; }
  11.                     th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  12.                     th { background-color: #f2f2f2; }
  13.                     tr:nth-child(even) { background-color: #f9f9f9; }
  14.                 </style>
  15.             </head>
  16.             <body>
  17.                 <h2>员工信息表</h2>
  18.                 <table>
  19.                     <tr>
  20.                         <th>ID</th>
  21.                         <th>姓名</th>
  22.                         <th>职位</th>
  23.                         <th>部门</th>
  24.                         <th>邮箱</th>
  25.                         <th>电话</th>
  26.                     </tr>
  27.                     <xsl:for-each select="company/employee">
  28.                         <tr>
  29.                             <td><xsl:value-of select="@id"/></td>
  30.                             <td><xsl:value-of select="name"/></td>
  31.                             <td><xsl:value-of select="position"/></td>
  32.                             <td><xsl:value-of select="department"/></td>
  33.                             <td><xsl:value-of select="email"/></td>
  34.                             <td><xsl:value-of select="phone"/></td>
  35.                         </tr>
  36.                     </xsl:for-each>
  37.                 </table>
  38.             </body>
  39.         </html>
  40.     </xsl:template>
  41. </xsl:stylesheet>
复制代码

最后,我们使用C#代码执行转换:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. public class XmlToHtmlConverter
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 创建XslCompiledTransform对象
  12.             XslCompiledTransform xslt = new XslCompiledTransform();
  13.             
  14.             // 加载XSLT样式表
  15.             xslt.Load("employees_to_html.xslt");
  16.             
  17.             // 执行转换
  18.             string inputFile = "employees.xml";
  19.             string outputFile = "employees.html";
  20.             
  21.             xslt.Transform(inputFile, outputFile);
  22.             
  23.             Console.WriteLine($"转换成功!输出文件:{outputFile}");
  24.         }
  25.         catch (Exception ex)
  26.         {
  27.             Console.WriteLine($"转换过程中发生错误:{ex.Message}");
  28.         }
  29.     }
  30. }
复制代码

4.2 XML到XML的转换

XSLT不仅可以用于将XML转换为HTML,还可以用于XML到XML的转换,这在数据重组和格式标准化中非常有用。

假设我们需要将上述员工信息XML转换为另一种格式的XML,其中部门作为父元素,员工作为子元素:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:output method="xml" indent="yes"/>
  4.    
  5.     <xsl:key name="dept-key" match="employee" use="department" />
  6.    
  7.     <xsl:template match="/">
  8.         <organization>
  9.             <xsl:for-each select="company/employee[generate-id() = generate-id(key('dept-key', department)[1])]">
  10.                 <xsl:sort select="department" />
  11.                 <department name="{department}">
  12.                     <xsl:for-each select="key('dept-key', department)">
  13.                         <xsl:sort select="name" />
  14.                         <employee id="{@id}">
  15.                             <name><xsl:value-of select="name"/></name>
  16.                             <position><xsl:value-of select="position"/></position>
  17.                             <email><xsl:value-of select="email"/></email>
  18.                             <phone><xsl:value-of select="phone"/></phone>
  19.                         </employee>
  20.                     </xsl:for-each>
  21.                 </department>
  22.             </xsl:for-each>
  23.         </organization>
  24.     </xsl:template>
  25. </xsl:stylesheet>
复制代码

对应的C#转换代码:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. public class XmlToXmlConverter
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 创建XslCompiledTransform对象
  12.             XslCompiledTransform xslt = new XslCompiledTransform();
  13.             
  14.             // 加载XSLT样式表
  15.             xslt.Load("reorganize_employees.xslt");
  16.             
  17.             // 使用XmlWriter设置输出格式
  18.             XmlWriterSettings settings = new XmlWriterSettings();
  19.             settings.Indent = true;
  20.             settings.IndentChars = "    ";
  21.             
  22.             // 执行转换
  23.             using (XmlReader input = XmlReader.Create("employees.xml"))
  24.             using (XmlWriter output = XmlWriter.Create("reorganized_employees.xml", settings))
  25.             {
  26.                 xslt.Transform(input, output);
  27.             }
  28.             
  29.             Console.WriteLine("XML重组成功!");
  30.         }
  31.         catch (Exception ex)
  32.         {
  33.             Console.WriteLine($"转换过程中发生错误:{ex.Message}");
  34.         }
  35.     }
  36. }
复制代码

4.3 使用XPath进行高级查询

XPath是XSLT的重要组成部分,它提供了强大的XML节点查询能力。在.NET中,我们可以通过System.Xml.XPath命名空间中的类来利用XPath功能。

以下示例展示了如何在XSLT中使用XPath进行复杂查询:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:output method="html" indent="yes"/>
  4.    
  5.     <xsl:template match="/">
  6.         <html>
  7.             <body>
  8.                 <h2>研发部员工信息</h2>
  9.                 <table border="1">
  10.                     <tr>
  11.                         <th>姓名</th>
  12.                         <th>职位</th>
  13.                         <th>邮箱</th>
  14.                     </tr>
  15.                     <!-- 使用XPath选择特定部门的员工 -->
  16.                     <xsl:for-each select="company/employee[department='研发部']">
  17.                         <xsl:sort select="name"/>
  18.                         <tr>
  19.                             <td><xsl:value-of select="name"/></td>
  20.                             <td><xsl:value-of select="position"/></td>
  21.                             <td><xsl:value-of select="email"/></td>
  22.                         </tr>
  23.                     </xsl:for-each>
  24.                 </table>
  25.                
  26.                 <h2>职位包含"工程师"的员工</h2>
  27.                 <ul>
  28.                     <!-- 使用XPath的contains函数 -->
  29.                     <xsl:for-each select="company/employee[contains(position, '工程师')]">
  30.                         <li><xsl:value-of select="name"/> - <xsl:value-of select="position"/></li>
  31.                     </xsl:for-each>
  32.                 </ul>
  33.             </body>
  34.         </html>
  35.     </xsl:template>
  36. </xsl:stylesheet>
复制代码

在.NET代码中,我们也可以使用XPathDocument和XPathNavigator来预查询XML数据:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.XPath;
  4. using System.Xml.Xsl;
  5. public class XPathWithXslt
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 加载XML文档到XPathDocument
  12.             XPathDocument xpathDoc = new XPathDocument("employees.xml");
  13.             
  14.             // 创建XPathNavigator
  15.             XPathNavigator navigator = xpathDoc.CreateNavigator();
  16.             
  17.             // 使用XPath查询特定节点
  18.             XPathNodeIterator iterator = navigator.Select("//employee[department='研发部']");
  19.             
  20.             Console.WriteLine("研发部员工:");
  21.             while (iterator.MoveNext())
  22.             {
  23.                 XPathNavigator currentNode = iterator.Current;
  24.                 Console.WriteLine($"- {currentNode.SelectSingleNode("name").Value}");
  25.             }
  26.             
  27.             // 执行XSLT转换
  28.             XslCompiledTransform xslt = new XslCompiledTransform();
  29.             xslt.Load("xpath_query.xslt");
  30.             
  31.             xslt.Transform("employees.xml", "xpath_result.html");
  32.             
  33.             Console.WriteLine("XPath查询和XSLT转换完成!");
  34.         }
  35.         catch (Exception ex)
  36.         {
  37.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  38.         }
  39.     }
  40. }
复制代码

5. 高级XSLT技术

5.1 使用XSLT扩展函数

XSLT本身提供了丰富的函数库,但有时我们需要使用.NET中的自定义函数来扩展XSLT的功能。这可以通过扩展对象实现。

首先,我们创建一个包含自定义函数的C#类:
  1. using System;
  2. using System.Globalization;
  3. namespace XsltExtensions
  4. {
  5.     public class StringExtensions
  6.     {
  7.         // 将字符串转换为大写
  8.         public string ToUpper(string input)
  9.         {
  10.             return input?.ToUpper();
  11.         }
  12.         
  13.         // 格式化日期
  14.         public string FormatDate(string dateString, string format)
  15.         {
  16.             if (DateTime.TryParse(dateString, out DateTime date))
  17.             {
  18.                 return date.ToString(format);
  19.             }
  20.             return dateString;
  21.         }
  22.         
  23.         // 计算字符串长度
  24.         public int GetLength(string input)
  25.         {
  26.             return input?.Length ?? 0;
  27.         }
  28.     }
  29.    
  30.     public class MathExtensions
  31.     {
  32.         // 计算折扣价
  33.         public decimal CalculateDiscount(decimal price, decimal discountPercent)
  34.         {
  35.             return price * (1 - discountPercent / 100);
  36.         }
  37.         
  38.         // 计算税额
  39.         public decimal CalculateTax(decimal amount, decimal taxRate)
  40.         {
  41.             return amount * (taxRate / 100);
  42.         }
  43.     }
  44. }
复制代码

然后,在XSLT中使用这些扩展函数:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.     xmlns:str="urn:XsltExtensions.StringExtensions"
  4.     xmlns:math="urn:XsltExtensions.MathExtensions"
  5.     exclude-result-prefixes="str math">
  6.    
  7.     <xsl:output method="html" indent="yes"/>
  8.    
  9.     <xsl:template match="/">
  10.         <html>
  11.             <body>
  12.                 <h2>产品信息(使用扩展函数)</h2>
  13.                 <table border="1">
  14.                     <tr>
  15.                         <th>产品名称</th>
  16.                         <th>原价</th>
  17.                         <th>折扣价</th>
  18.                         <th>税额</th>
  19.                         <th>名称长度</th>
  20.                     </tr>
  21.                     <xsl:for-each select="products/product">
  22.                         <tr>
  23.                             <td><xsl:value-of select="str:ToUpper(name)"/></td>
  24.                             <td><xsl:value-of select="price"/></td>
  25.                             <td><xsl:value-of select="math:CalculateDiscount(price, discount)"/></td>
  26.                             <td><xsl:value-of select="math:CalculateTax(price, 10)"/></td>
  27.                             <td><xsl:value-of select="str:GetLength(name)"/></td>
  28.                         </tr>
  29.                     </xsl:for-each>
  30.                 </table>
  31.             </body>
  32.         </html>
  33.     </xsl:template>
  34. </xsl:stylesheet>
复制代码

最后,在C#代码中将扩展对象传递给XSLT转换:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using XsltExtensions;
  5. public class XsltWithExtensions
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 创建XslCompiledTransform对象
  12.             XslCompiledTransform xslt = new XslCompiledTransform();
  13.             
  14.             // 加载XSLT样式表
  15.             xslt.Load("extensions.xslt");
  16.             
  17.             // 创建XsltArgumentList并添加扩展对象
  18.             XsltArgumentList argsList = new XsltArgumentList();
  19.             argsList.AddExtensionObject("urn:XsltExtensions.StringExtensions", new StringExtensions());
  20.             argsList.AddExtensionObject("urn:XsltExtensions.MathExtensions", new MathExtensions());
  21.             
  22.             // 执行转换,传递扩展对象
  23.             using (XmlReader input = XmlReader.Create("products.xml"))
  24.             using (XmlWriter output = XmlWriter.Create("products_with_extensions.html"))
  25.             {
  26.                 xslt.Transform(input, argsList, output);
  27.             }
  28.             
  29.             Console.WriteLine("使用扩展函数的XSLT转换完成!");
  30.         }
  31.         catch (Exception ex)
  32.         {
  33.             Console.WriteLine($"转换过程中发生错误:{ex.Message}");
  34.         }
  35.     }
  36. }
复制代码

5.2 多文档处理

XSLT不仅可以处理单个XML文档,还可以同时处理多个XML文档,实现数据的合并和关联。

假设我们有两个XML文件:employees.xml和departments.xml。

employees.xml:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <company>
  3.     <employee id="1" deptId="101">
  4.         <name>张三</name>
  5.         <position>软件工程师</position>
  6.         <email>zhangsan@example.com</email>
  7.     </employee>
  8.     <employee id="2" deptId="102">
  9.         <name>李四</name>
  10.         <position>产品经理</position>
  11.         <email>lisi@example.com</email>
  12.     </employee>
  13.     <employee id="3" deptId="101">
  14.         <name>王五</name>
  15.         <position>高级软件工程师</position>
  16.         <email>wangwu@example.com</email>
  17.     </employee>
  18. </company>
复制代码

departments.xml:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <departments>
  3.     <department id="101">
  4.         <name>研发部</name>
  5.         <location>北京</location>
  6.         <manager>赵六</manager>
  7.     </department>
  8.     <department id="102">
  9.         <name>产品部</name>
  10.         <location>上海</location>
  11.         <manager>钱七</manager>
  12.     </department>
  13.     <department id="103">
  14.         <name>市场部</name>
  15.         <location>广州</location>
  16.         <manager>孙八</manager>
  17.     </department>
  18. </departments>
复制代码

现在,我们创建一个XSLT样式表,将这两个文档的信息合并:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:output method="html" indent="yes"/>
  4.    
  5.     <!-- 加载部门文档 -->
  6.     <xsl:variable name="departments" select="document('departments.xml')"/>
  7.    
  8.     <xsl:template match="/">
  9.         <html>
  10.             <body>
  11.                 <h2>员工与部门信息</h2>
  12.                 <table border="1">
  13.                     <tr>
  14.                         <th>员工ID</th>
  15.                         <th>姓名</th>
  16.                         <th>职位</th>
  17.                         <th>部门</th>
  18.                         <th>部门位置</th>
  19.                         <th>部门经理</th>
  20.                     </tr>
  21.                     <xsl:for-each select="company/employee">
  22.                         <xsl:variable name="deptId" select="@deptId"/>
  23.                         <tr>
  24.                             <td><xsl:value-of select="@id"/></td>
  25.                             <td><xsl:value-of select="name"/></td>
  26.                             <td><xsl:value-of select="position"/></td>
  27.                             <!-- 从部门文档中获取部门信息 -->
  28.                             <td><xsl:value-of select="$departments/departments/department[@id=$deptId]/name"/></td>
  29.                             <td><xsl:value-of select="$departments/departments/department[@id=$deptId]/location"/></td>
  30.                             <td><xsl:value-of select="$departments/departments/department[@id=$deptId]/manager"/></td>
  31.                         </tr>
  32.                     </xsl:for-each>
  33.                 </table>
  34.             </body>
  35.         </html>
  36.     </xsl:template>
  37. </xsl:stylesheet>
复制代码

在.NET代码中,我们需要确保XSLT可以找到departments.xml文件:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. public class MultiDocumentProcessing
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 创建XslCompiledTransform对象
  12.             XslCompiledTransform xslt = new XslCompiledTransform();
  13.             
  14.             // 加载XSLT样式表
  15.             xslt.Load("multi_doc.xslt");
  16.             
  17.             // 创建XmlResolver以解析外部文档
  18.             XmlUrlResolver resolver = new XmlUrlResolver();
  19.             
  20.             // 执行转换
  21.             using (XmlReader input = XmlReader.Create("employees.xml"))
  22.             using (XmlWriter output = XmlWriter.Create("employee_department_info.html"))
  23.             {
  24.                 xslt.Transform(input, null, output, resolver);
  25.             }
  26.             
  27.             Console.WriteLine("多文档处理完成!");
  28.         }
  29.         catch (Exception ex)
  30.         {
  31.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  32.         }
  33.     }
  34. }
复制代码

5.3 使用XSLT生成动态内容

XSLT不仅可以用于静态转换,还可以生成包含动态内容的输出,例如根据输入数据生成不同的HTML元素或JavaScript代码。

以下示例展示了如何使用XSLT生成一个包含交互式图表的HTML页面:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  3.     <xsl:output method="html" indent="yes"/>
  4.    
  5.     <xsl:template match="/">
  6.         <html>
  7.             <head>
  8.                 <title>销售数据图表</title>
  9.                 <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  10.             </head>
  11.             <body>
  12.                 <h1>月度销售数据</h1>
  13.                 <div style="width: 80%; margin: 0 auto;">
  14.                     <canvas id="salesChart"></canvas>
  15.                 </div>
  16.                
  17.                 <script>
  18.                     // 使用XSLT生成JavaScript代码
  19.                     var ctx = document.getElementById('salesChart').getContext('2d');
  20.                     var salesChart = new Chart(ctx, {
  21.                         type: 'bar',
  22.                         data: {
  23.                             labels: [
  24.                                 <xsl:for-each select="sales/month">
  25.                                     '<xsl:value-of select="@name"/>'<xsl:if test="position() != last()">,</xsl:if>
  26.                                 </xsl:for-each>
  27.                             ],
  28.                             datasets: [{
  29.                                 label: '销售额(万元)',
  30.                                 data: [
  31.                                     <xsl:for-each select="sales/month">
  32.                                         <xsl:value-of select="@amount"/><xsl:if test="position() != last()">,</xsl:if>
  33.                                     </xsl:for-each>
  34.                                 ],
  35.                                 backgroundColor: 'rgba(54, 162, 235, 0.2)',
  36.                                 borderColor: 'rgba(54, 162, 235, 1)',
  37.                                 borderWidth: 1
  38.                             }]
  39.                         },
  40.                         options: {
  41.                             scales: {
  42.                                 y: {
  43.                                     beginAtZero: true
  44.                                 }
  45.                             }
  46.                         }
  47.                     });
  48.                 </script>
  49.                
  50.                 <h2>详细数据</h2>
  51.                 <table border="1">
  52.                     <tr>
  53.                         <th>月份</th>
  54.                         <th>销售额(万元)</th>
  55.                         <th>同比增长</th>
  56.                     </tr>
  57.                     <xsl:for-each select="sales/month">
  58.                         <tr>
  59.                             <td><xsl:value-of select="@name"/></td>
  60.                             <td><xsl:value-of select="@amount"/></td>
  61.                             <td>
  62.                                 <xsl:choose>
  63.                                     <xsl:when test="@growth > 0">
  64.                                         <span style="color: green;">+<xsl:value-of select="@growth"/>%</span>
  65.                                     </xsl:when>
  66.                                     <xsl:when test="@growth < 0">
  67.                                         <span style="color: red;"><xsl:value-of select="@growth"/>%</span>
  68.                                     </xsl:when>
  69.                                     <xsl:otherwise>
  70.                                         <span>0%</span>
  71.                                     </xsl:otherwise>
  72.                                 </xsl:choose>
  73.                             </td>
  74.                         </tr>
  75.                     </xsl:for-each>
  76.                 </table>
  77.             </body>
  78.         </html>
  79.     </xsl:template>
  80. </xsl:stylesheet>
复制代码

对应的C#代码:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. public class DynamicContentGeneration
  5. {
  6.     public static void Main()
  7.     {
  8.         try
  9.         {
  10.             // 创建XslCompiledTransform对象
  11.             XslCompiledTransform xslt = new XslCompiledTransform();
  12.             
  13.             // 加载XSLT样式表
  14.             xslt.Load("dynamic_content.xslt");
  15.             
  16.             // 执行转换
  17.             xslt.Transform("sales_data.xml", "sales_chart.html");
  18.             
  19.             Console.WriteLine("动态内容生成完成!");
  20.             Console.WriteLine("请在浏览器中打开sales_chart.html查看结果。");
  21.         }
  22.         catch (Exception ex)
  23.         {
  24.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  25.         }
  26.     }
  27. }
复制代码

6. 性能优化策略

6.1 XSLT编译与缓存

在.NET中,XslCompiledTransform类提供了比旧版XslTransform更好的性能,因为它会将XSLT样式表编译为中间格式。对于频繁使用的XSLT样式表,可以将其编译并缓存起来,避免重复编译的开销。

以下是一个XSLT缓存管理器的实现:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. public class XsltCacheManager
  6. {
  7.     private static readonly Dictionary<string, XslCompiledTransform> _cache =
  8.         new Dictionary<string, XslCompiledTransform>();
  9.     private static readonly object _lockObj = new object();
  10.    
  11.     // 获取缓存的XSLT转换器
  12.     public static XslCompiledTransform GetCachedTransform(string xsltPath)
  13.     {
  14.         // 检查文件是否存在
  15.         if (!File.Exists(xsltPath))
  16.         {
  17.             throw new FileNotFoundException("XSLT文件未找到", xsltPath);
  18.         }
  19.         
  20.         // 获取文件最后修改时间
  21.         DateTime lastModified = File.GetLastWriteTime(xsltPath);
  22.         string cacheKey = $"{xsltPath}_{lastModified.Ticks}";
  23.         
  24.         // 尝试从缓存中获取
  25.         if (_cache.ContainsKey(cacheKey))
  26.         {
  27.             return _cache[cacheKey];
  28.         }
  29.         
  30.         // 缓存中不存在,则创建新的转换器
  31.         lock (_lockObj)
  32.         {
  33.             // 双重检查,防止在等待锁时其他线程已经创建了转换器
  34.             if (_cache.ContainsKey(cacheKey))
  35.             {
  36.                 return _cache[cacheKey];
  37.             }
  38.             
  39.             // 创建新的XslCompiledTransform
  40.             XslCompiledTransform transform = new XslCompiledTransform();
  41.             
  42.             // 加载XSLT样式表
  43.             try
  44.             {
  45.                 transform.Load(xsltPath);
  46.                
  47.                 // 添加到缓存
  48.                 _cache[cacheKey] = transform;
  49.                
  50.                 // 如果缓存过大,清理一些旧项
  51.                 if (_cache.Count > 20)
  52.                 {
  53.                     ClearOldCacheItems();
  54.                 }
  55.                
  56.                 return transform;
  57.             }
  58.             catch (Exception ex)
  59.             {
  60.                 throw new Exception($"加载XSLT文件失败: {xsltPath}", ex);
  61.             }
  62.         }
  63.     }
  64.    
  65.     // 清理旧的缓存项
  66.     private static void ClearOldCacheItems()
  67.     {
  68.         // 简单实现:保留最近使用的10个项
  69.         if (_cache.Count <= 10)
  70.         {
  71.             return;
  72.         }
  73.         
  74.         // 获取所有键
  75.         List<string> keys = new List<string>(_cache.Keys);
  76.         
  77.         // 按时间戳排序(键的格式为"path_timestamp")
  78.         keys.Sort((a, b) =>
  79.         {
  80.             long timestampA = long.Parse(a.Substring(a.LastIndexOf('_') + 1));
  81.             long timestampB = long.Parse(b.Substring(b.LastIndexOf('_') + 1));
  82.             return timestampA.CompareTo(timestampB);
  83.         });
  84.         
  85.         // 删除最旧的一半
  86.         for (int i = 0; i < keys.Count / 2; i++)
  87.         {
  88.             _cache.Remove(keys[i]);
  89.         }
  90.     }
  91.    
  92.     // 清除所有缓存
  93.     public static void ClearCache()
  94.     {
  95.         lock (_lockObj)
  96.         {
  97.             _cache.Clear();
  98.         }
  99.     }
  100. }
复制代码

使用缓存管理器的示例:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. public class XsltCacheExample
  5. {
  6.     public static void Main()
  7.     {
  8.         try
  9.         {
  10.             // 从缓存获取XSLT转换器
  11.             XslCompiledTransform xslt = XsltCacheManager.GetCachedTransform("transform.xslt");
  12.             
  13.             // 执行转换
  14.             xslt.Transform("input.xml", "output.html");
  15.             
  16.             Console.WriteLine("使用缓存的XSLT转换完成!");
  17.             
  18.             // 再次使用相同的XSLT文件,将从缓存中获取
  19.             XslCompiledTransform xslt2 = XsltCacheManager.GetCachedTransform("transform.xslt");
  20.             xslt2.Transform("input2.xml", "output2.html");
  21.             
  22.             Console.WriteLine("第二次转换完成,使用了缓存的XSLT转换器。");
  23.         }
  24.         catch (Exception ex)
  25.         {
  26.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  27.         }
  28.     }
  29. }
复制代码

6.2 优化XSLT样式表

编写高效的XSLT样式表对性能至关重要。以下是一些优化XSLT样式表的技巧:

1. 使用键(key)提高查询效率:对于频繁查询的节点,使用<xsl:key>定义键,然后通过key()函数访问。
  1. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  2.     <!-- 定义键 -->
  3.     <xsl:key name="employee-by-dept" match="employee" use="department" />
  4.    
  5.     <xsl:template match="/">
  6.         <!-- 使用键查询 -->
  7.         <xsl:for-each select="key('employee-by-dept', '研发部')">
  8.             <xsl:value-of select="name"/>
  9.         </xsl:for-each>
  10.     </xsl:template>
  11. </xsl:stylesheet>
复制代码

1. 避免使用//进行全局搜索:尽量使用具体的XPath表达式,减少搜索范围。
  1. <!-- 不推荐:全局搜索 -->
  2. <xsl:for-each select="//employee">
  3. <!-- 推荐:指定路径 -->
  4. <xsl:for-each select="company/department/employee">
复制代码

1. 使用模板匹配代替for-each:模板匹配通常比for-each更高效,特别是对于复杂的转换。
  1. <!-- 使用模板匹配 -->
  2. <xsl:apply-templates select="company/employee"/>
  3. <!-- 定义模板 -->
  4. <xsl:template match="employee">
  5.     <div>
  6.         <xsl:value-of select="name"/>
  7.     </div>
  8. </xsl:template>
复制代码

1. 减少不必要的输出:使用<xsl:output>的indent="no"属性可以减少输出大小,提高性能。
  1. <xsl:output method="xml" indent="no"/>
复制代码

1. 使用模式(mode)处理同一节点的多种转换:当需要对同一节点进行多种不同的处理时,使用模式可以避免复杂的条件判断。
  1. <xsl:template match="employee" mode="brief">
  2.     <xsl:value-of select="name"/>
  3. </xsl:template>
  4. <xsl:template match="employee" mode="detailed">
  5.     <div>
  6.         <p>姓名:<xsl:value-of select="name"/></p>
  7.         <p>职位:<xsl:value-of select="position"/></p>
  8.     </div>
  9. </xsl:template>
  10. <!-- 使用模式 -->
  11. <xsl:apply-templates select="employee" mode="brief"/>
复制代码

6.3 大文件处理策略

处理大型XML文件时,内存和性能可能成为问题。以下是一些处理大文件的策略:

1. 使用XmlReader进行流式处理:通过XmlReader逐节点读取XML,避免将整个文档加载到内存。
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. public class LargeFileProcessing
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 创建XslCompiledTransform对象
  12.             XslCompiledTransform xslt = new XslCompiledTransform();
  13.             
  14.             // 加载XSLT样式表
  15.             xslt.Load("transform.xslt");
  16.             
  17.             // 使用XmlReader和XmlWriter进行流式处理
  18.             XmlReaderSettings readerSettings = new XmlReaderSettings();
  19.             readerSettings.IgnoreWhitespace = true;
  20.             readerSettings.IgnoreComments = true;
  21.             
  22.             XmlWriterSettings writerSettings = new XmlWriterSettings();
  23.             writerSettings.Indent = true;
  24.             writerSettings.IndentChars = "    ";
  25.             
  26.             using (XmlReader reader = XmlReader.Create("large_input.xml", readerSettings))
  27.             using (XmlWriter writer = XmlWriter.Create("output.xml", writerSettings))
  28.             {
  29.                 xslt.Transform(reader, writer);
  30.             }
  31.             
  32.             Console.WriteLine("大文件处理完成!");
  33.         }
  34.         catch (Exception ex)
  35.         {
  36.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  37.         }
  38.     }
  39. }
复制代码

1. 分块处理:将大文件分成多个小块,分别处理后再合并结果。
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. public class ChunkedProcessing
  6. {
  7.     public static void Main()
  8.     {
  9.         try
  10.         {
  11.             // 创建XslCompiledTransform对象
  12.             XslCompiledTransform xslt = new XslCompiledTransform();
  13.             
  14.             // 加载XSLT样式表
  15.             xslt.Load("transform.xslt");
  16.             
  17.             // 输出文件
  18.             string outputFile = "merged_output.xml";
  19.             
  20.             // 使用XmlWriter创建输出文件
  21.             XmlWriterSettings writerSettings = new XmlWriterSettings();
  22.             writerSettings.Indent = true;
  23.             writerSettings.IndentChars = "    ";
  24.             
  25.             using (XmlWriter writer = XmlWriter.Create(outputFile, writerSettings))
  26.             {
  27.                 // 写入根元素开始标签
  28.                 writer.WriteStartElement("root");
  29.                
  30.                 // 处理每个输入文件
  31.                 string[] inputFiles = { "part1.xml", "part2.xml", "part3.xml" };
  32.                
  33.                 foreach (string inputFile in inputFiles)
  34.                 {
  35.                     Console.WriteLine($"处理文件: {inputFile}");
  36.                     
  37.                     // 创建临时结果
  38.                     string tempFile = Path.GetTempFileName();
  39.                     
  40.                     // 转换当前文件
  41.                     using (XmlReader reader = XmlReader.Create(inputFile))
  42.                     using (XmlWriter tempWriter = XmlWriter.Create(tempFile))
  43.                     {
  44.                         xslt.Transform(reader, tempWriter);
  45.                     }
  46.                     
  47.                     // 将临时结果追加到最终输出
  48.                     using (XmlReader tempReader = XmlReader.Create(tempFile))
  49.                     {
  50.                         tempReader.MoveToContent();
  51.                         
  52.                         // 跳过根元素
  53.                         if (tempReader.Read())
  54.                         {
  55.                             // 读取并写入所有子节点
  56.                             while (tempReader.Read() && tempReader.NodeType != XmlNodeType.EndElement)
  57.                             {
  58.                                 switch (tempReader.NodeType)
  59.                                 {
  60.                                     case XmlNodeType.Element:
  61.                                         writer.WriteStartElement(tempReader.Prefix, tempReader.LocalName, tempReader.NamespaceURI);
  62.                                         writer.WriteAttributes(tempReader, true);
  63.                                        
  64.                                         if (tempReader.IsEmptyElement)
  65.                                         {
  66.                                             writer.WriteEndElement();
  67.                                         }
  68.                                         else
  69.                                         {
  70.                                             // 写入子节点
  71.                                             if (tempReader.Read() && tempReader.NodeType == XmlNodeType.Text)
  72.                                             {
  73.                                                 writer.WriteString(tempReader.Value);
  74.                                             }
  75.                                             
  76.                                             // 写入结束元素
  77.                                             writer.WriteEndElement();
  78.                                         }
  79.                                         break;
  80.                                        
  81.                                     case XmlNodeType.Text:
  82.                                         writer.WriteString(tempReader.Value);
  83.                                         break;
  84.                                        
  85.                                     case XmlNodeType.CDATA:
  86.                                         writer.WriteCData(tempReader.Value);
  87.                                         break;
  88.                                        
  89.                                     case XmlNodeType.Comment:
  90.                                         writer.WriteComment(tempReader.Value);
  91.                                         break;
  92.                                 }
  93.                             }
  94.                         }
  95.                     }
  96.                     
  97.                     // 删除临时文件
  98.                     File.Delete(tempFile);
  99.                 }
  100.                
  101.                 // 写入根元素结束标签
  102.                 writer.WriteEndElement();
  103.             }
  104.             
  105.             Console.WriteLine($"分块处理完成!结果已保存到: {outputFile}");
  106.         }
  107.         catch (Exception ex)
  108.         {
  109.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  110.         }
  111.     }
  112. }
复制代码

1. 使用XslCompiledTransform的XsltSettings禁用不必要功能:通过XsltSettings可以禁用一些不必要的功能,提高性能。
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. public class XsltSettingsExample
  5. {
  6.     public static void Main()
  7.     {
  8.         try
  9.         {
  10.             // 创建XsltSettings,禁用不必要的功能
  11.             XsltSettings settings = new XsltSettings();
  12.             settings.EnableDocumentFunction = false;  // 禁用document()函数
  13.             settings.EnableScript = false;            // 禁用脚本
  14.             
  15.             // 创建XslCompiledTransform对象
  16.             XslCompiledTransform xslt = new XslCompiledTransform();
  17.             
  18.             // 加载XSLT样式表,应用设置
  19.             xslt.Load("transform.xslt", settings, new XmlUrlResolver());
  20.             
  21.             // 执行转换
  22.             xslt.Transform("input.xml", "output.html");
  23.             
  24.             Console.WriteLine("使用优化设置的XSLT转换完成!");
  25.         }
  26.         catch (Exception ex)
  27.         {
  28.             Console.WriteLine($"处理过程中发生错误:{ex.Message}");
  29.         }
  30.     }
  31. }
复制代码

7. 实战案例:复杂XML数据转换

7.1 电子商务产品目录转换

假设我们有一个电子商务平台,需要将供应商提供的XML产品目录转换为平台内部使用的格式,并生成HTML页面用于网站展示。

首先,让我们看看供应商提供的XML格式(supplier_products.xml):
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <ProductCatalog>
  3.     <Supplier>
  4.         <ID>SP001</ID>
  5.         <Name>优质供应商</Name>
  6.         <Contact>张经理</Contact>
  7.         <Phone>010-12345678</Phone>
  8.     </Supplier>
  9.     <Products>
  10.         <Product SKU="P1001">
  11.             <Name>智能手机</Name>
  12.             <Description>高性能智能手机,配备先进的处理器和摄像头</Description>
  13.             <Category>电子产品</Category>
  14.             <SubCategory>手机</SubCategory>
  15.             <Price Currency="CNY">2999.00</Price>
  16.             <Stock>100</Stock>
  17.             <Specifications>
  18.                 <Spec Name="屏幕尺寸">6.5英寸</Spec>
  19.                 <Spec Name="内存">8GB</Spec>
  20.                 <Spec Name="存储">128GB</Spec>
  21.                 <Spec Name="电池容量">4000mAh</Spec>
  22.             </Specifications>
  23.             <Images>
  24.                 <Image URL="https://example.com/images/p1001_1.jpg" IsPrimary="true"/>
  25.                 <Image URL="https://example.com/images/p1001_2.jpg"/>
  26.                 <Image URL="https://example.com/images/p1001_3.jpg"/>
  27.             </Images>
  28.         </Product>
  29.         <Product SKU="P1002">
  30.             <Name>笔记本电脑</Name>
  31.             <Description>轻薄便携的笔记本电脑,适合办公和学习</Description>
  32.             <Category>电子产品</Category>
  33.             <SubCategory>电脑</SubCategory>
  34.             <Price Currency="CNY">5999.00</Price>
  35.             <Stock>50</Stock>
  36.             <Specifications>
  37.                 <Spec Name="屏幕尺寸">14英寸</Spec>
  38.                 <Spec Name="处理器">Intel i5</Spec>
  39.                 <Spec Name="内存">16GB</Spec>
  40.                 <Spec Name="存储">512GB SSD</Spec>
  41.             </Specifications>
  42.             <Images>
  43.                 <Image URL="https://example.com/images/p1002_1.jpg" IsPrimary="true"/>
  44.                 <Image URL="https://example.com/images/p1002_2.jpg"/>
  45.             </Images>
  46.         </Product>
  47.         <Product SKU="P1003">
  48.             <Name>无线耳机</Name>
  49.             <Description>高品质无线蓝牙耳机,降噪功能强大</Description>
  50.             <Category>电子产品</Category>
  51.             <SubCategory>音频设备</SubCategory>
  52.             <Price Currency="CNY">899.00</Price>
  53.             <Stock>200</Stock>
  54.             <Specifications>
  55.                 <Spec Name="连接方式">蓝牙5.0</Spec>
  56.                 <Spec Name="电池续航">20小时</Spec>
  57.                 <Spec Name="降噪">主动降噪</Spec>
  58.                 <Spec Name="防水等级">IPX4</Spec>
  59.             </Specifications>
  60.             <Images>
  61.                 <Image URL="https://example.com/images/p1003_1.jpg" IsPrimary="true"/>
  62.             </Images>
  63.         </Product>
  64.     </Products>
  65. </ProductCatalog>
复制代码

我们需要将其转换为平台内部使用的格式(internal_products.xml):
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Inventory>
  3.     <ImportDate>2023-11-15</ImportDate>
  4.     <Source>
  5.         <SupplierID>SP001</SupplierID>
  6.         <SupplierName>优质供应商</SupplierName>
  7.     </Source>
  8.     <Items>
  9.         <Item InternalID="ITM001">
  10.             <SKU>P1001</SKU>
  11.             <Title>智能手机</Title>
  12.             <ShortDescription>高性能智能手机</ShortDescription>
  13.             <FullDescription>高性能智能手机,配备先进的处理器和摄像头</FullDescription>
  14.             <Category>
  15.                 <Main>电子产品</Main>
  16.                 <Sub>手机</Sub>
  17.             </Category>
  18.             <Pricing>
  19.                 <ListPrice Currency="CNY">2999.00</ListPrice>
  20.                 <DiscountPrice Currency="CNY">2699.10</DiscountPrice>
  21.             </Pricing>
  22.             <Inventory>
  23.                 <Available>100</Available>
  24.                 <Status>InStock</Status>
  25.             </Inventory>
  26.             <Attributes>
  27.                 <Attribute Name="屏幕尺寸">6.5英寸</Attribute>
  28.                 <Attribute Name="内存">8GB</Attribute>
  29.                 <Attribute Name="存储">128GB</Attribute>
  30.                 <Attribute Name="电池容量">4000mAh</Attribute>
  31.             </Attributes>
  32.             <Media>
  33.                 <PrimaryImage>https://example.com/images/p1001_1.jpg</PrimaryImage>
  34.                 <AdditionalImages>
  35.                     <Image>https://example.com/images/p1001_2.jpg</Image>
  36.                     <Image>https://example.com/images/p1001_3.jpg</Image>
  37.                 </AdditionalImages>
  38.             </Media>
  39.         </Item>
  40.         <!-- 其他产品... -->
  41.     </Items>
  42. </Inventory>
复制代码

同时,我们还需要生成一个HTML页面用于网站展示(products.html)。

让我们创建XSLT样式表来完成这些转换:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.     xmlns:ext="urn:ecommerce-extensions" exclude-result-prefixes="ext">
  4.    
  5.     <xsl:output method="xml" indent="yes" name="xml-format"/>
  6.     <xsl:output method="html" indent="yes" name="html-format"/>
  7.    
  8.     <!-- 生成内部XML格式 -->
  9.     <xsl:template match="/">
  10.         <xsl:result-document href="internal_products.xml" format="xml-format">
  11.             <Inventory>
  12.                 <ImportDate><xsl:value-of select="ext:CurrentDate()"/></ImportDate>
  13.                 <Source>
  14.                     <SupplierID><xsl:value-of select="ProductCatalog/Supplier/ID"/></SupplierID>
  15.                     <SupplierName><xsl:value-of select="ProductCatalog/Supplier/Name"/></SupplierName>
  16.                 </Source>
  17.                 <Items>
  18.                     <xsl:for-each select="ProductCatalog/Products/Product">
  19.                         <Item InternalID="{ext:GenerateInternalID(position())}">
  20.                             <SKU><xsl:value-of select="@SKU"/></SKU>
  21.                             <Title><xsl:value-of select="Name"/></Title>
  22.                             <ShortDescription><xsl:value-of select="substring(Description, 1, 50)"/>...</ShortDescription>
  23.                             <FullDescription><xsl:value-of select="Description"/></FullDescription>
  24.                             <Category>
  25.                                 <Main><xsl:value-of select="Category"/></Main>
  26.                                 <Sub><xsl:value-of select="SubCategory"/></Sub>
  27.                             </Category>
  28.                             <Pricing>
  29.                                 <ListPrice Currency="{Price/@Currency}"><xsl:value-of select="Price"/></ListPrice>
  30.                                 <DiscountPrice Currency="{Price/@Currency}"><xsl:value-of select="ext:CalculateDiscount(Price, 10)"/></DiscountPrice>
  31.                             </Pricing>
  32.                             <Inventory>
  33.                                 <Available><xsl:value-of select="Stock"/></Available>
  34.                                 <Status>
  35.                                     <xsl:choose>
  36.                                         <xsl:when test="Stock > 0">InStock</xsl:when>
  37.                                         <xsl:otherwise>OutOfStock</xsl:otherwise>
  38.                                     </xsl:choose>
  39.                                 </Status>
  40.                             </Inventory>
  41.                             <Attributes>
  42.                                 <xsl:for-each select="Specifications/Spec">
  43.                                     <Attribute Name="{@Name}"><xsl:value-of select="."/></Attribute>
  44.                                 </xsl:for-each>
  45.                             </Attributes>
  46.                             <Media>
  47.                                 <PrimaryImage><xsl:value-of select="Images/Image[@IsPrimary='true']/@URL"/></PrimaryImage>
  48.                                 <AdditionalImages>
  49.                                     <xsl:for-each select="Images/Image[not(@IsPrimary='true')]">
  50.                                         <Image><xsl:value-of select="@URL"/></Image>
  51.                                     </xsl:for-each>
  52.                                 </AdditionalImages>
  53.                             </Media>
  54.                         </Item>
  55.                     </xsl:for-each>
  56.                 </Items>
  57.             </Inventory>
  58.         </xsl:result-document>
  59.         
  60.         <!-- 生成HTML页面 -->
  61.         <xsl:result-document href="products.html" format="html-format">
  62.             <html>
  63.                 <head>
  64.                     <title>产品目录 - <xsl:value-of select="ProductCatalog/Supplier/Name"/></title>
  65.                     <style>
  66.                         body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
  67.                         .header { background-color: #333; color: white; padding: 20px; text-align: center; }
  68.                         .product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-top: 20px; }
  69.                         .product-card { background-color: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden; }
  70.                         .product-image { width: 100%; height: 200px; object-fit: cover; }
  71.                         .product-info { padding: 15px; }
  72.                         .product-title { font-size: 1.2em; font-weight: bold; margin-bottom: 10px; }
  73.                         .product-price { color: #e44d26; font-weight: bold; font-size: 1.1em; }
  74.                         .product-description { color: #666; margin-top: 10px; }
  75.                         .product-specs { margin-top: 15px; }
  76.                         .spec-item { margin-bottom: 5px; }
  77.                         .spec-name { font-weight: bold; }
  78.                         .stock-status { margin-top: 10px; padding: 5px; border-radius: 4px; text-align: center; }
  79.                         .in-stock { background-color: #d4edda; color: #155724; }
  80.                         .out-of-stock { background-color: #f8d7da; color: #721c24; }
  81.                     </style>
  82.                 </head>
  83.                 <body>
  84.                     <div class="header">
  85.                         <h1><xsl:value-of select="ProductCatalog/Supplier/Name"/>产品目录</h1>
  86.                         <p>更新日期: <xsl:value-of select="ext:CurrentDate()"/></p>
  87.                     </div>
  88.                     
  89.                     <div class="product-grid">
  90.                         <xsl:for-each select="ProductCatalog/Products/Product">
  91.                             <div class="product-card">
  92.                                 <img class="product-image" src="{Images/Image[@IsPrimary='true']/@URL}" alt="{Name}"/>
  93.                                 <div class="product-info">
  94.                                     <div class="product-title"><xsl:value-of select="Name"/></div>
  95.                                     <div class="product-price">¥<xsl:value-of select="ext:CalculateDiscount(Price, 10)"/>
  96.                                         <span style="text-decoration: line-through; color: #999; font-size: 0.9em;">¥<xsl:value-of select="Price"/></span>
  97.                                     </div>
  98.                                     <div class="product-description"><xsl:value-of select="substring(Description, 1, 80)"/>...</div>
  99.                                     
  100.                                     <div class="product-specs">
  101.                                         <xsl:for-each select="Specifications/Spec[position() &lt;= 3]">
  102.                                             <div class="spec-item">
  103.                                                 <span class="spec-name"><xsl:value-of select="@Name"/>:</span>
  104.                                                 <xsl:value-of select="."/>
  105.                                             </div>
  106.                                         </xsl:for-each>
  107.                                     </div>
  108.                                     
  109.                                     <div class="stock-status">
  110.                                         <xsl:attribute name="class">
  111.                                             <xsl:choose>
  112.                                                 <xsl:when test="Stock > 0">stock-status in-stock</xsl:when>
  113.                                                 <xsl:otherwise">stock-status out-of-stock</xsl:otherwise>
  114.                                             </xsl:choose>
  115.                                         </xsl:attribute>
  116.                                         <xsl:choose>
  117.                                             <xsl:when test="Stock > 0">有货 (<xsl:value-of select="Stock"/>)</xsl:when>
  118.                                             <xsl:otherwise>暂时缺货</xsl:otherwise>
  119.                                         </xsl:choose>
  120.                                     </div>
  121.                                 </div>
  122.                             </div>
  123.                         </xsl:for-each>
  124.                     </div>
  125.                 </body>
  126.             </html>
  127.         </xsl:result-document>
  128.     </xsl:template>
  129. </xsl:stylesheet>
复制代码

现在,我们需要创建扩展类来提供自定义函数:
  1. using System;
  2. using System.Globalization;
  3. namespace ECommerceExtensions
  4. {
  5.     public class XsltExtensions
  6.     {
  7.         // 获取当前日期
  8.         public string CurrentDate()
  9.         {
  10.             return DateTime.Now.ToString("yyyy-MM-dd");
  11.         }
  12.         
  13.         // 生成内部ID
  14.         public string GenerateInternalID(int position)
  15.         {
  16.             return $"ITM{position:D3}";
  17.         }
  18.         
  19.         // 计算折扣价
  20.         public decimal CalculateDiscount(string priceString, decimal discountPercent)
  21.         {
  22.             if (decimal.TryParse(priceString, NumberStyles.Any, CultureInfo.InvariantCulture, out decimal price))
  23.             {
  24.                 decimal discountAmount = price * (discountPercent / 100);
  25.                 return price - discountAmount;
  26.             }
  27.             return 0;
  28.         }
  29.     }
  30. }
复制代码

最后,我们创建C#代码来执行转换:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. using ECommerceExtensions;
  6. public class ECommerceProductTransformation
  7. {
  8.     public static void Main()
  9.     {
  10.         try
  11.         {
  12.             Console.WriteLine("开始电子商务产品目录转换...");
  13.             
  14.             // 创建XslCompiledTransform对象
  15.             XslCompiledTransform xslt = new XslCompiledTransform();
  16.             
  17.             // 加载XSLT样式表
  18.             xslt.Load("product_transform.xslt");
  19.             
  20.             // 创建XsltArgumentList并添加扩展对象
  21.             XsltArgumentList argsList = new XsltArgumentList();
  22.             argsList.AddExtensionObject("urn:ecommerce-extensions", new XsltExtensions());
  23.             
  24.             // 确保输出目录存在
  25.             string outputDir = "output";
  26.             if (!Directory.Exists(outputDir))
  27.             {
  28.                 Directory.CreateDirectory(outputDir);
  29.             }
  30.             
  31.             // 设置当前工作目录为输出目录
  32.             string originalDir = Directory.GetCurrentDirectory();
  33.             Directory.SetCurrentDirectory(outputDir);
  34.             
  35.             try
  36.             {
  37.                 // 执行转换
  38.                 using (XmlReader input = XmlReader.Create(Path.Combine(originalDir, "supplier_products.xml")))
  39.                 {
  40.                     xslt.Transform(input, argsList);
  41.                 }
  42.                
  43.                 Console.WriteLine("转换完成!");
  44.                 Console.WriteLine($"生成的文件:");
  45.                 Console.WriteLine($"- {Path.Combine(outputDir, "internal_products.xml")}");
  46.                 Console.WriteLine($"- {Path.Combine(outputDir, "products.html")}");
  47.             }
  48.             finally
  49.             {
  50.                 // 恢复原始工作目录
  51.                 Directory.SetCurrentDirectory(originalDir);
  52.             }
  53.         }
  54.         catch (Exception ex)
  55.         {
  56.             Console.WriteLine($"转换过程中发生错误:{ex.Message}");
  57.             Console.WriteLine(ex.StackTrace);
  58.         }
  59.     }
  60. }
复制代码

这个实战案例展示了如何使用XSLT技术进行复杂的XML数据转换,包括:

1. 将供应商XML格式转换为内部系统格式
2. 同时生成HTML页面用于网站展示
3. 使用扩展函数提供额外的功能
4. 处理条件逻辑和数据计算
5. 生成多个输出文件

7.2 企业报表生成

在企业应用中,经常需要从各种数据源生成报表。XSLT可以用于将XML数据转换为各种格式的报表,如HTML、PDF(通过XSL-FO)或Excel(通过HTML表格)。

假设我们有以下销售数据XML(sales_data.xml):
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <SalesData>
  3.     <Period>
  4.         <Year>2023</Year>
  5.         <Quarter>Q3</Quarter>
  6.         <StartDate>2023-07-01</StartDate>
  7.         <EndDate>2023-09-30</EndDate>
  8.     </Period>
  9.     <Regions>
  10.         <Region ID="R001">
  11.             <Name>华北区</Name>
  12.             <Manager>王经理</Name>
  13.             <Sales>
  14.                 <Month Month="7">
  15.                     <Product Category="电子产品" ProductID="P001">1200000</Product>
  16.                     <Product Category="家电" ProductID="P002">850000</Product>
  17.                     <Product Category="服装" ProductID="P003">650000</Product>
  18.                     <Product Category="食品" ProductID="P004">480000</Product>
  19.                 </Month>
  20.                 <Month Month="8">
  21.                     <Product Category="电子产品" ProductID="P001">1350000</Product>
  22.                     <Product Category="家电" ProductID="P002">920000</Product>
  23.                     <Product Category="服装" ProductID="P003">720000</Product>
  24.                     <Product Category="食品" ProductID="P004">510000</Product>
  25.                 </Month>
  26.                 <Month Month="9">
  27.                     <Product Category="电子产品" ProductID="P001">1500000</Product>
  28.                     <Product Category="家电" ProductID="P002">1050000</Product>
  29.                     <Product Category="服装" ProductID="P003">830000</Product>
  30.                     <Product Category="食品" ProductID="P004">580000</Product>
  31.                 </Month>
  32.             </Sales>
  33.         </Region>
  34.         <Region ID="R002">
  35.             <Name>华东区</Name>
  36.             <Manager>李经理</Manager>
  37.             <Sales>
  38.                 <Month Month="7">
  39.                     <Product Category="电子产品" ProductID="P001">1800000</Product>
  40.                     <Product Category="家电" ProductID="P002">1200000</Product>
  41.                     <Product Category="服装" ProductID="P003">950000</Product>
  42.                     <Product Category="食品" ProductID="P004">720000</Product>
  43.                 </Month>
  44.                 <Month Month="8">
  45.                     <Product Category="电子产品" ProductID="P001">1950000</Product>
  46.                     <Product Category="家电" ProductID="P002">1350000</Product>
  47.                     <Product Category="服装" ProductID="P003">1100000</Product>
  48.                     <Product Category="食品" ProductID="P004">850000</Product>
  49.                 </Month>
  50.                 <Month Month="9">
  51.                     <Product Category="电子产品" ProductID="P001">2100000</Product>
  52.                     <Product Category="家电" ProductID="P002">1500000</Product>
  53.                     <Product Category="服装" ProductID="P003">1250000</Product>
  54.                     <Product Category="食品" ProductID="P004">980000</Product>
  55.                 </Month>
  56.             </Sales>
  57.         </Region>
  58.         <Region ID="R003">
  59.             <Name>华南区</Name>
  60.             <Manager>张经理</Manager>
  61.             <Sales>
  62.                 <Month Month="7">
  63.                     <Product Category="电子产品" ProductID="P001">1500000</Product>
  64.                     <Product Category="家电" ProductID="P002">1100000</Product>
  65.                     <Product Category="服装" ProductID="P003">850000</Product>
  66.                     <Product Category="食品" ProductID="P004">650000</Product>
  67.                 </Month>
  68.                 <Month Month="8">
  69.                     <Product Category="电子产品" ProductID="P001">1650000</Product>
  70.                     <Product Category="家电" ProductID="P002">1250000</Product>
  71.                     <Product Category="服装" ProductID="P003">950000</Product>
  72.                     <Product Category="食品" ProductID="P004">750000</Product>
  73.                 </Month>
  74.                 <Month Month="9">
  75.                     <Product Category="电子产品" ProductID="P001">1800000</Product>
  76.                     <Product Category="家电" ProductID="P002">1400000</Product>
  77.                     <Product Category="服装" ProductID="P003">1100000</Product>
  78.                     <Product Category="食品" ProductID="P004">900000</Product>
  79.                 </Month>
  80.             </Sales>
  81.         </Region>
  82.     </Regions>
  83. </SalesData>
复制代码

我们需要生成以下几种格式的报表:

1. HTML格式的详细报表
2. 可导入Excel的HTML表格
3. 汇总XML数据

让我们创建XSLT样式表(sales_report.xslt):
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  3.     xmlns:ext="urn:sales-extensions" exclude-result-prefixes="ext">
  4.    
  5.     <xsl:output method="html" indent="yes" name="html-format" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
  6.     <xsl:output method="html" indent="yes" name="excel-format"
  7.         doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
  8.         doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
  9.     <xsl:output method="xml" indent="yes" name="xml-format"/>
  10.    
  11.     <xsl:template match="/">
  12.         <!-- 生成HTML详细报表 -->
  13.         <xsl:result-document href="sales_report.html" format="html-format">
  14.             <html>
  15.                 <head>
  16.                     <title>销售报表 - <xsl:value-of select="SalesData/Period/Year"/>年<xsl:value-of select="SalesData/Period/Quarter"/></title>
  17.                     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  18.                     <style>
  19.                         body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
  20.                         .header { background-color: #2c3e50; color: white; padding: 20px; text-align: center; border-radius: 5px; margin-bottom: 20px; }
  21.                         .summary { background-color: white; padding: 15px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }
  22.                         .summary-table { width: 100%; border-collapse: collapse; }
  23.                         .summary-table th, .summary-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  24.                         .summary-table th { background-color: #f2f2f2; }
  25.                         .region-section { background-color: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; }
  26.                         .region-title { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; margin-bottom: 15px; }
  27.                         .sales-table { width: 100%; border-collapse: collapse; }
  28.                         .sales-table th, .sales-table td { border: 1px solid #ddd; padding: 8px; text-align: right; }
  29.                         .sales-table th { background-color: #3498db; color: white; text-align: center; }
  30.                         .sales-table td:first-child { text-align: left; }
  31.                         .sales-table .total-row { font-weight: bold; background-color: #f2f2f2; }
  32.                         .chart-container { margin-top: 20px; height: 300px; }
  33.                         .footer { text-align: center; margin-top: 30px; color: #7f8c8d; font-size: 0.9em; }
  34.                     </style>
  35.                     <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  36.                 </head>
  37.                 <body>
  38.                     <div class="header">
  39.                         <h1>销售报表</h1>
  40.                         <p><xsl:value-of select="SalesData/Period/Year"/>年<xsl:value-of select="SalesData/Period/Quarter"/>季度</p>
  41.                         <p>报表日期: <xsl:value-of select="ext:CurrentDate()"/></p>
  42.                     </div>
  43.                     
  44.                     <div class="summary">
  45.                         <h2>销售汇总</h2>
  46.                         <table class="summary-table">
  47.                             <tr>
  48.                                 <th>区域</th>
  49.                                 <th>总销售额(元)</th>
  50.                                 <th>月均销售额(元)</th>
  51.                                 <th>最佳销售月份</th>
  52.                                 <th>最佳销售产品类别</th>
  53.                             </tr>
  54.                             <xsl:for-each select="SalesData/Regions/Region">
  55.                                 <xsl:variable name="regionData" select="."/>
  56.                                 <xsl:variable name="totalSales" select="ext:CalculateRegionTotalSales($regionData)"/>
  57.                                 <xsl:variable name="avgSales" select="$totalSales div count($regionData/Sales/Month)"/>
  58.                                 <xsl:variable name="bestMonth" select="ext:GetBestMonth($regionData)"/>
  59.                                 <xsl:variable name="bestCategory" select="ext:GetBestCategory($regionData)"/>
  60.                                 
  61.                                 <tr>
  62.                                     <td><xsl:value-of select="Name"/></td>
  63.                                     <td><xsl:value-of select="format-number($totalSales, '###,###,##0.00')"/></td>
  64.                                     <td><xsl:value-of select="format-number($avgSales, '###,###,##0.00')"/></td>
  65.                                     <td><xsl:value-of select="$bestMonth"/>月</td>
  66.                                     <td><xsl:value-of select="$bestCategory"/></td>
  67.                                 </tr>
  68.                             </xsl:for-each>
  69.                         </table>
  70.                     </div>
  71.                     
  72.                     <xsl:for-each select="SalesData/Regions/Region">
  73.                         <div class="region-section">
  74.                             <h2 class="region-title"><xsl:value-of select="Name"/> - 区域经理: <xsl:value-of select="Manager"/></h2>
  75.                            
  76.                             <table class="sales-table">
  77.                                 <tr>
  78.                                     <th>产品类别</th>
  79.                                     <xsl:for-each select="Sales/Month">
  80.                                         <th><xsl:value-of select="@Month"/>月</th>
  81.                                     </xsl:for-each>
  82.                                     <th>季度总计</th>
  83.                                 </tr>
  84.                                 
  85.                                 <xsl:variable name="regionData" select="."/>
  86.                                 <xsl:for-each select="Sales/Month[1]/Product">
  87.                                     <xsl:variable name="category" select="@Category"/>
  88.                                     <tr>
  89.                                         <td><xsl:value-of select="$category"/></td>
  90.                                         <xsl:for-each select="$regionData/Sales/Month">
  91.                                             <td>
  92.                                                 <xsl:value-of select="format-number(Product[@Category=$category], '###,###,##0.00')"/>
  93.                                             </td>
  94.                                         </xsl:for-each>
  95.                                         <td>
  96.                                             <xsl:value-of select="format-number(ext:CalculateCategoryTotal($regionData, $category), '###,###,##0.00')"/>
  97.                                         </td>
  98.                                     </tr>
  99.                                 </xsl:for-each>
  100.                                 
  101.                                 <tr class="total-row">
  102.                                     <td>月度总计</td>
  103.                                     <xsl:for-each select="Sales/Month">
  104.                                         <td>
  105.                                             <xsl:value-of select="format-number(ext:CalculateMonthTotal(.), '###,###,##0.00')"/>
  106.                                         </td>
  107.                                     </xsl:for-each>
  108.                                     <td>
  109.                                         <xsl:value-of select="format-number(ext:CalculateRegionTotalSales($regionData), '###,###,##0.00')"/>
  110.                                     </td>
  111.                                 </tr>
  112.                             </table>
  113.                            
  114.                             <div class="chart-container">
  115.                                 <canvas id="chart{position()}"></canvas>
  116.                             </div>
  117.                            
  118.                             <script>
  119.                                 var ctx<xsl:value-of select="position()"/> = document.getElementById('chart<xsl:value-of select="position()"/>').getContext('2d');
  120.                                 var chart<xsl:value-of select="position()"/> = new Chart(ctx<xsl:value-of select="position()"/>, {
  121.                                     type: 'line',
  122.                                     data: {
  123.                                         labels: [
  124.                                             <xsl:for-each select="Sales/Month">
  125.                                                 '<xsl:value-of select="@Month"/>月'<xsl:if test="position() != last()">,</xsl:if>
  126.                                             </xsl:for-each>
  127.                                         ],
  128.                                         datasets: [
  129.                                             <xsl:for-each select="Sales/Month[1]/Product">
  130.                                                 {
  131.                                                     label: '<xsl:value-of select="@Category"/>',
  132.                                                     data: [
  133.                                                         <xsl:variable name="category" select="@Category"/>
  134.                                                         <xsl:for-each select="$regionData/Sales/Month">
  135.                                                             <xsl:value-of select="Product[@Category=$category]"/><xsl:if test="position() != last()">,</xsl:if>
  136.                                                         </xsl:for-each>
  137.                                                     ],
  138.                                                     borderColor: '<xsl:value-of select="ext:GetColor(position())"/>',
  139.                                                     backgroundColor: 'rgba(0, 0, 0, 0)',
  140.                                                     tension: 0.1
  141.                                                 }<xsl:if test="position() != last()">,</xsl:if>
  142.                                             </xsl:for-each>
  143.                                         ]
  144.                                     },
  145.                                     options: {
  146.                                         responsive: true,
  147.                                         maintainAspectRatio: false,
  148.                                         plugins: {
  149.                                             title: {
  150.                                                 display: true,
  151.                                                 text: '<xsl:value-of select="Name"/>销售趋势'
  152.                                             }
  153.                                         },
  154.                                         scales: {
  155.                                             y: {
  156.                                                 beginAtZero: true,
  157.                                                 title: {
  158.                                                     display: true,
  159.                                                     text: '销售额(元)'
  160.                                                 }
  161.                                             }
  162.                                         }
  163.                                     }
  164.                                 });
  165.                             </script>
  166.                         </div>
  167.                     </xsl:for-each>
  168.                     
  169.                     <div class="footer">
  170.                         <p>本报表由系统自动生成 | 生成时间: <xsl:value-of select="ext:CurrentDateTime()"/></p>
  171.                     </div>
  172.                 </body>
  173.             </html>
  174.         </xsl:result-document>
  175.         
  176.         <!-- 生成Excel格式报表 -->
  177.         <xsl:result-document href="sales_report_excel.html" format="excel-format">
  178.             <html xmlns:o="urn:schemas-microsoft-com:office:office"
  179.                   xmlns:x="urn:schemas-microsoft-com:office:excel"
  180.                   xmlns="http://www.w3.org/TR/REC-html40">
  181.                 <head>
  182.                     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  183.                     <!--[if gte mso 9]>
  184.                     <xml>
  185.                         <x:ExcelWorkbook>
  186.                             <x:ExcelWorksheets>
  187.                                 <x:ExcelWorksheet>
  188.                                     <x:Name>销售数据</x:Name>
  189.                                     <x:WorksheetOptions>
  190.                                         <x:Print>
  191.                                             <x:ValidPrinterInfo/>
  192.                                         </x:Print>
  193.                                     </x:WorksheetOptions>
  194.                                 </x:ExcelWorksheet>
  195.                             </x:ExcelWorksheets>
  196.                         </x:ExcelWorkbook>
  197.                     </xml>
  198.                     <![endif]-->
  199.                 </head>
  200.                 <body>
  201.                     <table>
  202.                         <tr>
  203.                             <td colspan="7" style="font-weight: bold; text-align: center; font-size: 16pt;">
  204.                                 销售报表 - <xsl:value-of select="SalesData/Period/Year"/>年<xsl:value-of select="SalesData/Period/Quarter"/>
  205.                             </td>
  206.                         </tr>
  207.                         <tr>
  208.                             <td colspan="7" style="text-align: center;">
  209.                                 报表日期: <xsl:value-of select="ext:CurrentDate()"/>
  210.                             </td>
  211.                         </tr>
  212.                         <tr>
  213.                             <td colspan="7"> </td>
  214.                         </tr>
  215.                         
  216.                         <xsl:for-each select="SalesData/Regions/Region">
  217.                             <xsl:variable name="regionData" select="."/>
  218.                            
  219.                             <tr>
  220.                                 <td colspan="7" style="font-weight: bold; background-color: #3498db; color: white;">
  221.                                     <xsl:value-of select="Name"/> - 区域经理: <xsl:value-of select="Manager"/>
  222.                                 </td>
  223.                             </tr>
  224.                            
  225.                             <tr>
  226.                                 <td style="font-weight: bold; background-color: #f2f2f2;">产品类别</td>
  227.                                 <xsl:for-each select="Sales/Month">
  228.                                     <td style="font-weight: bold; background-color: #f2f2f2; text-align: center;">
  229.                                         <xsl:value-of select="@Month"/>月
  230.                                     </td>
  231.                                 </xsl:for-each>
  232.                                 <td style="font-weight: bold; background-color: #f2f2f2; text-align: center;">季度总计</td>
  233.                             </tr>
  234.                            
  235.                             <xsl:for-each select="Sales/Month[1]/Product">
  236.                                 <xsl:variable name="category" select="@Category"/>
  237.                                 <tr>
  238.                                     <td><xsl:value-of select="$category"/></td>
  239.                                     <xsl:for-each select="$regionData/Sales/Month">
  240.                                         <td style="mso-number-format: '\#\,\#\#0\.00'">
  241.                                             <xsl:value-of select="Product[@Category=$category]"/>
  242.                                         </td>
  243.                                     </xsl:for-each>
  244.                                     <td style="mso-number-format: '\#\,\#\#0\.00'; font-weight: bold;">
  245.                                         <xsl:value-of select="ext:CalculateCategoryTotal($regionData, $category)"/>
  246.                                     </td>
  247.                                 </tr>
  248.                             </xsl:for-each>
  249.                            
  250.                             <tr>
  251.                                 <td style="font-weight: bold; background-color: #f2f2f2;">月度总计</td>
  252.                                 <xsl:for-each select="Sales/Month">
  253.                                     <td style="mso-number-format: '\#\,\#\#0\.00'; font-weight: bold; background-color: #f2f2f2;">
  254.                                         <xsl:value-of select="ext:CalculateMonthTotal(.)"/>
  255.                                     </td>
  256.                                 </xsl:for-each>
  257.                                 <td style="mso-number-format: '\#\,\#\#0\.00'; font-weight: bold; background-color: #f2f2f2;">
  258.                                     <xsl:value-of select="ext:CalculateRegionTotalSales($regionData)"/>
  259.                                 </td>
  260.                             </tr>
  261.                            
  262.                             <tr>
  263.                                 <td colspan="7"> </td>
  264.                             </tr>
  265.                         </xsl:for-each>
  266.                         
  267.                         <tr>
  268.                             <td colspan="7" style="font-weight: bold; background-color: #2c3e50; color: white;">
  269.                                 销售汇总
  270.                             </td>
  271.                         </tr>
  272.                         
  273.                         <tr>
  274.                             <td style="font-weight: bold; background-color: #f2f2f2;">区域</td>
  275.                             <td style="font-weight: bold; background-color: #f2f2f2;">总销售额(元)</td>
  276.                             <td style="font-weight: bold; background-color: #f2f2f2;">月均销售额(元)</td>
  277.                             <td style="font-weight: bold; background-color: #f2f2f2;">最佳销售月份</td>
  278.                             <td style="font-weight: bold; background-color: #f2f2f2;">最佳销售产品类别</td>
  279.                             <td style="font-weight: bold; background-color: #f2f2f2;">同比增长</td>
  280.                             <td style="font-weight: bold; background-color: #f2f2f2;">市场份额</td>
  281.                         </tr>
  282.                         
  283.                         <xsl:variable name="grandTotal" select="ext:CalculateGrandTotal(/SalesData)"/>
  284.                         
  285.                         <xsl:for-each select="SalesData/Regions/Region">
  286.                             <xsl:variable name="regionData" select="."/>
  287.                             <xsl:variable name="totalSales" select="ext:CalculateRegionTotalSales($regionData)"/>
  288.                             <xsl:variable name="avgSales" select="$totalSales div count($regionData/Sales/Month)"/>
  289.                             <xsl:variable name="bestMonth" select="ext:GetBestMonth($regionData)"/>
  290.                             <xsl:variable name="bestCategory" select="ext:GetBestCategory($regionData)"/>
  291.                             <xsl:variable name="growth" select="ext:CalculateGrowth($regionData, 15)"/>
  292.                             <xsl:variable name="marketShare" select="($totalSales div $grandTotal) * 100"/>
  293.                            
  294.                             <tr>
  295.                                 <td><xsl:value-of select="Name"/></td>
  296.                                 <td style="mso-number-format: '\#\,\#\#0\.00'"><xsl:value-of select="$totalSales"/></td>
  297.                                 <td style="mso-number-format: '\#\,\#\#0\.00'"><xsl:value-of select="$avgSales"/></td>
  298.                                 <td><xsl:value-of select="$bestMonth"/>月</td>
  299.                                 <td><xsl:value-of select="$bestCategory"/></td>
  300.                                 <td style="mso-number-format: '0\.00\%'"><xsl:value-of select="$growth"/></td>
  301.                                 <td style="mso-number-format: '0\.00\%'"><xsl:value-of select="$marketShare"/></td>
  302.                             </tr>
  303.                         </xsl:for-each>
  304.                         
  305.                         <tr>
  306.                             <td style="font-weight: bold; background-color: #f2f2f2;">总计</td>
  307.                             <td style="mso-number-format: '\#\,\#\#0\.00'; font-weight: bold; background-color: #f2f2f2;">
  308.                                 <xsl:value-of select="$grandTotal"/>
  309.                             </td>
  310.                             <td style="mso-number-format: '\#\,\#\#0\.00'; font-weight: bold; background-color: #f2f2f2;">
  311.                                 <xsl:value-of select="$grandTotal div count(/SalesData/Regions/Region/Sales/Month)"/>
  312.                             </td>
  313.                             <td colspan="4" style="background-color: #f2f2f2;"> </td>
  314.                         </tr>
  315.                     </table>
  316.                 </body>
  317.             </html>
  318.         </xsl:result-document>
  319.         
  320.         <!-- 生成汇总XML数据 -->
  321.         <xsl:result-document href="sales_summary.xml" format="xml-format">
  322.             <SalesSummary>
  323.                 <Period>
  324.                     <Year><xsl:value-of select="SalesData/Period/Year"/></Year>
  325.                     <Quarter><xsl:value-of select="SalesData/Period/Quarter"/></Quarter>
  326.                     <StartDate><xsl:value-of select="SalesData/Period/StartDate"/></StartDate>
  327.                     <EndDate><xsl:value-of select="SalesData/Period/EndDate"/></EndDate>
  328.                     <ReportDate><xsl:value-of select="ext:CurrentDate()"/></ReportDate>
  329.                 </Period>
  330.                
  331.                 <GrandTotal>
  332.                     <Amount><xsl:value-of select="ext:CalculateGrandTotal(/SalesData)"/></Amount>
  333.                     <RegionCount><xsl:value-of select="count(SalesData/Regions/Region)"/></RegionCount>
  334.                     <MonthCount><xsl:value-of select="count(SalesData/Regions/Region[1]/Sales/Month)"/></MonthCount>
  335.                 </GrandTotal>
  336.                
  337.                 <RegionSummaries>
  338.                     <xsl:for-each select="SalesData/Regions/Region">
  339.                         <xsl:variable name="regionData" select="."/>
  340.                         <xsl:variable name="totalSales" select="ext:CalculateRegionTotalSales($regionData)"/>
  341.                         <xsl:variable name="avgSales" select="$totalSales div count($regionData/Sales/Month)"/>
  342.                         <xsl:variable name="bestMonth" select="ext:GetBestMonth($regionData)"/>
  343.                         <xsl:variable name="bestCategory" select="ext:GetBestCategory($regionData)"/>
  344.                         <xsl:variable name="growth" select="ext:CalculateGrowth($regionData, 15)"/>
  345.                         
  346.                         <RegionSummary ID="{@ID}">
  347.                             <Name><xsl:value-of select="Name"/></Name>
  348.                             <Manager><xsl:value-of select="Manager"/></Manager>
  349.                             <TotalSales><xsl:value-of select="$totalSales"/></TotalSales>
  350.                             <AverageSales><xsl:value-of select="$avgSales"/></AverageSales>
  351.                             <BestMonth><xsl:value-of select="$bestMonth"/></BestMonth>
  352.                             <BestCategory><xsl:value-of select="$bestCategory"/></BestCategory>
  353.                             <GrowthRate><xsl:value-of select="$growth"/></GrowthRate>
  354.                             <MarketShare>
  355.                                 <xsl:value-of select="($totalSales div ext:CalculateGrandTotal(/SalesData)) * 100"/>
  356.                             </MarketShare>
  357.                            
  358.                             <CategoryBreakdown>
  359.                                 <xsl:for-each select="Sales/Month[1]/Product">
  360.                                     <xsl:variable name="category" select="@Category"/>
  361.                                     <Category>
  362.                                         <Name><xsl:value-of select="$category"/></Name>
  363.                                         <Total><xsl:value-of select="ext:CalculateCategoryTotal($regionData, $category)"/></Total>
  364.                                         <Percentage>
  365.                                             <xsl:value-of select="(ext:CalculateCategoryTotal($regionData, $category) div $totalSales) * 100"/>
  366.                                         </Percentage>
  367.                                     </Category>
  368.                                 </xsl:for-each>
  369.                             </CategoryBreakdown>
  370.                            
  371.                             <MonthlyBreakdown>
  372.                                 <xsl:for-each select="Sales/Month">
  373.                                     <Month Number="{@Month}">
  374.                                         <Total><xsl:value-of select="ext:CalculateMonthTotal(.)"/></Total>
  375.                                         <BestCategory>
  376.                                             <xsl:value-of select="ext:GetBestCategoryForMonth(.)"/>
  377.                                         </BestCategory>
  378.                                     </Month>
  379.                                 </xsl:for-each>
  380.                             </MonthlyBreakdown>
  381.                         </RegionSummary>
  382.                     </xsl:for-each>
  383.                 </RegionSummaries>
  384.             </SalesSummary>
  385.         </xsl:result-document>
  386.     </xsl:template>
  387. </xsl:stylesheet>
复制代码

现在,我们需要创建扩展类来提供计算功能:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.XPath;
  4. namespace SalesExtensions
  5. {
  6.     public class SalesReportExtensions
  7.     {
  8.         // 获取当前日期
  9.         public string CurrentDate()
  10.         {
  11.             return DateTime.Now.ToString("yyyy-MM-dd");
  12.         }
  13.         
  14.         // 获取当前日期时间
  15.         public string CurrentDateTime()
  16.         {
  17.             return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  18.         }
  19.         
  20.         // 计算区域总销售额
  21.         public decimal CalculateRegionTotalSales(XPathNavigator region)
  22.         {
  23.             decimal total = 0;
  24.             XPathNodeIterator months = region.Select("Sales/Month");
  25.             
  26.             while (months.MoveNext())
  27.             {
  28.                 XPathNavigator month = months.Current;
  29.                 XPathNodeIterator products = month.Select("Product");
  30.                
  31.                 while (products.MoveNext())
  32.                 {
  33.                     if (decimal.TryParse(products.Current.Value, out decimal amount))
  34.                     {
  35.                         total += amount;
  36.                     }
  37.                 }
  38.             }
  39.             
  40.             return total;
  41.         }
  42.         
  43.         // 计算类别总销售额
  44.         public decimal CalculateCategoryTotal(XPathNavigator region, string category)
  45.         {
  46.             decimal total = 0;
  47.             XPathNodeIterator months = region.Select("Sales/Month");
  48.             
  49.             while (months.MoveNext())
  50.             {
  51.                 XPathNavigator month = months.Current;
  52.                 XPathNavigator product = month.SelectSingleNode($"Product[@Category='{category}']");
  53.                
  54.                 if (product != null && decimal.TryParse(product.Value, out decimal amount))
  55.                 {
  56.                     total += amount;
  57.                 }
  58.             }
  59.             
  60.             return total;
  61.         }
  62.         
  63.         // 计算月度总销售额
  64.         public decimal CalculateMonthTotal(XPathNavigator month)
  65.         {
  66.             decimal total = 0;
  67.             XPathNodeIterator products = month.Select("Product");
  68.             
  69.             while (products.MoveNext())
  70.             {
  71.                 if (decimal.TryParse(products.Current.Value, out decimal amount))
  72.                 {
  73.                     total += amount;
  74.                 }
  75.             }
  76.             
  77.             return total;
  78.         }
  79.         
  80.         // 获取最佳销售月份
  81.         public int GetBestMonth(XPathNavigator region)
  82.         {
  83.             decimal maxAmount = 0;
  84.             int bestMonth = 1;
  85.             
  86.             XPathNodeIterator months = region.Select("Sales/Month");
  87.             while (months.MoveNext())
  88.             {
  89.                 XPathNavigator month = months.Current;
  90.                 decimal monthTotal = CalculateMonthTotal(month);
  91.                
  92.                 if (monthTotal > maxAmount)
  93.                 {
  94.                     maxAmount = monthTotal;
  95.                     if (int.TryParse(month.GetAttribute("Month", ""), out int monthNum))
  96.                     {
  97.                         bestMonth = monthNum;
  98.                     }
  99.                 }
  100.             }
  101.             
  102.             return bestMonth;
  103.         }
  104.         
  105.         // 获取最佳销售类别
  106.         public string GetBestCategory(XPathNavigator region)
  107.         {
  108.             decimal maxAmount = 0;
  109.             string bestCategory = "";
  110.             
  111.             // 获取第一个月份的所有类别
  112.             XPathNavigator firstMonth = region.SelectSingleNode("Sales/Month[1]");
  113.             if (firstMonth != null)
  114.             {
  115.                 XPathNodeIterator products = firstMonth.Select("Product");
  116.                
  117.                 while (products.MoveNext())
  118.                 {
  119.                     string category = products.Current.GetAttribute("Category", "");
  120.                     decimal categoryTotal = CalculateCategoryTotal(region, category);
  121.                     
  122.                     if (categoryTotal > maxAmount)
  123.                     {
  124.                         maxAmount = categoryTotal;
  125.                         bestCategory = category;
  126.                     }
  127.                 }
  128.             }
  129.             
  130.             return bestCategory;
  131.         }
  132.         
  133.         // 获取指定月份的最佳销售类别
  134.         public string GetBestCategoryForMonth(XPathNavigator month)
  135.         {
  136.             decimal maxAmount = 0;
  137.             string bestCategory = "";
  138.             
  139.             XPathNodeIterator products = month.Select("Product");
  140.             while (products.MoveNext())
  141.             {
  142.                 if (decimal.TryParse(products.Current.Value, out decimal amount) && amount > maxAmount)
  143.                 {
  144.                     maxAmount = amount;
  145.                     bestCategory = products.Current.GetAttribute("Category", "");
  146.                 }
  147.             }
  148.             
  149.             return bestCategory;
  150.         }
  151.         
  152.         // 计算同比增长率
  153.         public decimal CalculateGrowth(XPathNavigator region, decimal baseGrowth)
  154.         {
  155.             // 简化实现:基于总销售额计算一个模拟增长率
  156.             decimal total = CalculateRegionTotalSales(region);
  157.             
  158.             // 模拟增长率计算:基于总销售额和基础增长率
  159.             // 实际应用中,应该与去年同期数据比较
  160.             return baseGrowth + (total / 10000000);  // 简化计算
  161.         }
  162.         
  163.         // 计算总销售额
  164.         public decimal CalculateGrandTotal(XPathNavigator salesData)
  165.         {
  166.             decimal total = 0;
  167.             XPathNodeIterator regions = salesData.Select("SalesData/Regions/Region");
  168.             
  169.             while (regions.MoveNext())
  170.             {
  171.                 total += CalculateRegionTotalSales(regions.Current);
  172.             }
  173.             
  174.             return total;
  175.         }
  176.         
  177.         // 获取图表颜色
  178.         public string GetColor(int index)
  179.         {
  180.             string[] colors = {
  181.                 "#FF6384", "#36A2EB", "#FFCE56", "#4BC0C0",
  182.                 "#9966FF", "#FF9F40", "#8AC926", "#1982C4",
  183.                 "#6A4C93", "#F15BB5"
  184.             };
  185.             
  186.             return colors[index % colors.Length];
  187.         }
  188.     }
  189. }
复制代码

最后,我们创建C#代码来执行转换:
  1. using System;
  2. using System.Xml;
  3. using System.Xml.Xsl;
  4. using System.IO;
  5. using SalesExtensions;
  6. public class SalesReportGenerator
  7. {
  8.     public static void Main()
  9.     {
  10.         try
  11.         {
  12.             Console.WriteLine("开始生成销售报表...");
  13.             
  14.             // 创建XslCompiledTransform对象
  15.             XslCompiledTransform xslt = new XslCompiledTransform();
  16.             
  17.             // 加载XSLT样式表
  18.             xslt.Load("sales_report.xslt");
  19.             
  20.             // 创建XsltArgumentList并添加扩展对象
  21.             XsltArgumentList argsList = new XsltArgumentList();
  22.             argsList.AddExtensionObject("urn:sales-extensions", new SalesReportExtensions());
  23.             
  24.             // 确保输出目录存在
  25.             string outputDir = "reports";
  26.             if (!Directory.Exists(outputDir))
  27.             {
  28.                 Directory.CreateDirectory(outputDir);
  29.             }
  30.             
  31.             // 设置当前工作目录为输出目录
  32.             string originalDir = Directory.GetCurrentDirectory();
  33.             Directory.SetCurrentDirectory(outputDir);
  34.             
  35.             try
  36.             {
  37.                 // 执行转换
  38.                 using (XmlReader input = XmlReader.Create(Path.Combine(originalDir, "sales_data.xml")))
  39.                 {
  40.                     xslt.Transform(input, argsList);
  41.                 }
  42.                
  43.                 Console.WriteLine("报表生成完成!");
  44.                 Console.WriteLine($"生成的文件:");
  45.                 Console.WriteLine($"- {Path.Combine(outputDir, "sales_report.html")} - 详细HTML报表");
  46.                 Console.WriteLine($"- {Path.Combine(outputDir, "sales_report_excel.html")} - Excel格式报表");
  47.                 Console.WriteLine($"- {Path.Combine(outputDir, "sales_summary.xml")} - 汇总XML数据");
  48.                
  49.                 Console.WriteLine("\n提示:");
  50.                 Console.WriteLine("- 在浏览器中打开 sales_report.html 查看详细报表");
  51.                 Console.WriteLine("- 在Excel中打开 sales_report_excel.html 查看可编辑的表格");
  52.                 Console.WriteLine("- sales_summary.xml 可用于其他系统集成");
  53.             }
  54.             finally
  55.             {
  56.                 // 恢复原始工作目录
  57.                 Directory.SetCurrentDirectory(originalDir);
  58.             }
  59.         }
  60.         catch (Exception ex)
  61.         {
  62.             Console.WriteLine($"生成报表过程中发生错误:{ex.Message}");
  63.             Console.WriteLine(ex.StackTrace);
  64.         }
  65.     }
  66. }
复制代码

这个实战案例展示了如何使用XSLT技术生成复杂的企业报表,包括:

1. 生成详细的HTML报表,包含图表和交互式元素
2. 生成可导入Excel的HTML表格,保留数字格式
3. 生成汇总XML数据,用于系统集成
4. 使用扩展函数进行复杂的数据计算和分析
5. 同时生成多种格式的输出文件

8. 常见问题与解决方案

8.1 XSLT转换性能问题

问题:处理大型XML文件时,XSLT转换速度很慢,甚至导致内存不足。

解决方案:

1.
  1. 使用XslCompiledTransform而不是XslTransform:
  2. “`csharp
  3. // 正确做法
  4. XslCompiledTransform xslt = new XslCompiledTransform();
  5. xslt.Load(“stylesheet.xslt”);
复制代码

// 错误做法(已过时)
   // XslTransform xslt = new XslTransform();
   // xslt.Load(“stylesheet.xslt”);
  1. 2. **优化XSLT样式表**:
  2.    ```xml
  3.    <!-- 使用键提高查询效率 -->
  4.    <xsl:key name="product-by-category" match="product" use="category" />
  5.    
  6.    <!-- 避免使用//进行全局搜索 -->
  7.    <!-- 不推荐 -->
  8.    <xsl:for-each select="//product">
  9.    
  10.    <!-- 推荐 -->
  11.    <xsl:for-each select="products/product">
复制代码

1.
  1. 使用流式处理:using (XmlReader reader = XmlReader.Create("large_file.xml"))
  2. using (XmlWriter writer = XmlWriter.Create("output.xml"))
  3. {
  4.    xslt.Transform(reader, writer);
  5. }
复制代码
2.
  1. 禁用不必要的XSLT功能:
  2. “`csharp
  3. XsltSettings settings = new XsltSettings();
  4. settings.EnableDocumentFunction = false;  // 禁用document()函数
  5. settings.EnableScript = false;            // 禁用脚本
复制代码

使用流式处理:
  1. using (XmlReader reader = XmlReader.Create("large_file.xml"))
  2. using (XmlWriter writer = XmlWriter.Create("output.xml"))
  3. {
  4.    xslt.Transform(reader, writer);
  5. }
复制代码

禁用不必要的XSLT功能:
“`csharp
XsltSettings settings = new XsltSettings();
settings.EnableDocumentFunction = false;  // 禁用document()函数
settings.EnableScript = false;            // 禁用脚本

XslCompiledTransform xslt = new XslCompiledTransform();
   xslt.Load(“stylesheet.xslt”, settings, new XmlUrlResolver());
  1. ### 8.2 命名空间处理问题
  2. **问题**:XML文档包含命名空间,导致XPath表达式无法正确匹配节点。
  3. **解决方案**:
  4. 1. **在XSLT中声明命名空间**:
  5.    ```xml
  6.    <xsl:stylesheet version="1.0"
  7.                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  8.                    xmlns:ns="http://example.com/namespace">
  9.       
  10.        <xsl:template match="/">
  11.            <xsl:for-each select="ns:root/ns:item">
  12.                <xsl:value-of select="ns:name"/>
  13.            </xsl:for-each>
  14.        </xsl:template>
  15.    </xsl:stylesheet>
复制代码

1.
  1. 使用本地名称()函数忽略命名空间:<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  2.    <xsl:template match="/">
  3.        <xsl:for-each select="*[local-name()='root']/*[local-name()='item']">
  4.            <xsl:value-of select="*[local-name()='name']"/>
  5.        </xsl:for-each>
  6.    </xsl:template>
  7. </xsl:stylesheet>
复制代码
2.
  1. 在C#代码中处理命名空间:
  2. “`csharp
  3. // 创建XmlNamespaceManager并添加命名空间
  4. XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable());
  5. nsManager.AddNamespace(“ns”, “http://example.com/namespace”);
复制代码

使用本地名称()函数忽略命名空间:
  1. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  2.    <xsl:template match="/">
  3.        <xsl:for-each select="*[local-name()='root']/*[local-name()='item']">
  4.            <xsl:value-of select="*[local-name()='name']"/>
  5.        </xsl:for-each>
  6.    </xsl:template>
  7. </xsl:stylesheet>
复制代码

在C#代码中处理命名空间:
“`csharp
// 创建XmlNamespaceManager并添加命名空间
XmlNamespaceManager nsManager = new XmlNamespaceManager(new NameTable());
nsManager.AddNamespace(“ns”, “http://example.com/namespace”);

// 在XPath查询中使用命名空间
   XPathNodeIterator nodes = navigator.Select(“//ns:root/ns:item”, nsManager);
  1. ### 8.3 特殊字符处理问题
  2. **问题**:XML数据中包含特殊字符(如<, >, &, ", '),导致转换失败或输出不正确。
  3. **解决方案**:
  4. 1. **确保XML数据正确转义**:
  5.    ```xml
  6.    <!-- 正确 -->
  7.    <description>This is a &quot;test&quot; with 5 &lt; 10</description>
  8.    
  9.    <!-- 错误 -->
  10.    <description>This is a "test" with 5 < 10</description>
复制代码

1.
  1. 使用CDATA节处理大量特殊字符:<content><![CDATA[
  2.    <html>
  3.        <body>
  4.            <p>This contains many special characters: <, >, &, ", '</p>
  5.        </body>
  6.    </html>
  7. ]]></content>
复制代码
2. 在XSLT中使用disable-output-escaping:<xsl:value-of select="content" disable-output-escaping="yes"/>
3.
  1. 在C#代码中处理特殊字符:// 使用XmlWriter自动处理转义
  2. using (XmlWriter writer = XmlWriter.Create("output.xml"))
  3. {
  4.    writer.WriteElementString("description", "This is a "test" with 5 < 10");
  5. }
复制代码

使用CDATA节处理大量特殊字符:
  1. <content><![CDATA[
  2.    <html>
  3.        <body>
  4.            <p>This contains many special characters: <, >, &, ", '</p>
  5.        </body>
  6.    </html>
  7. ]]></content>
复制代码

在XSLT中使用disable-output-escaping:
  1. <xsl:value-of select="content" disable-output-escaping="yes"/>
复制代码

在C#代码中处理特殊字符:
  1. // 使用XmlWriter自动处理转义
  2. using (XmlWriter writer = XmlWriter.Create("output.xml"))
  3. {
  4.    writer.WriteElementString("description", "This is a "test" with 5 < 10");
  5. }
复制代码

8.4 日期和时间格式化问题

问题:需要将XML中的日期数据格式化为特定格式,但XSLT 1.0的日期格式化功能有限。

解决方案:

1.
  1. 使用扩展函数进行日期格式化:public class DateExtensions
  2. {
  3.    public string FormatDate(string dateString, string format)
  4.    {
  5.        if (DateTime.TryParse(dateString, out DateTime date))
  6.        {
  7.            return date.ToString(format);
  8.        }
  9.        return dateString;
  10.    }
  11. }
复制代码
2.
  1. 在XSLT中使用扩展函数:<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  2.                xmlns:date="urn:date-extensions" exclude-result-prefixes="date">
  3.    <xsl:template match="/">
  4.        <xsl:value-of select="date:FormatDate(order/date, 'yyyy-MM-dd')"/>
  5.    </xsl:template>
  6. </xsl:stylesheet>
复制代码
3.
  1. 在C#代码中添加扩展对象:
  2. “`csharp
  3. XsltArgumentList argsList = new XsltArgumentList();
  4. argsList.AddExtensionObject(“urn:date-extensions”, new DateExtensions());
复制代码

使用扩展函数进行日期格式化:
  1. public class DateExtensions
  2. {
  3.    public string FormatDate(string dateString, string format)
  4.    {
  5.        if (DateTime.TryParse(dateString, out DateTime date))
  6.        {
  7.            return date.ToString(format);
  8.        }
  9.        return dateString;
  10.    }
  11. }
复制代码

在XSLT中使用扩展函数:
  1. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  2.                xmlns:date="urn:date-extensions" exclude-result-prefixes="date">
  3.    <xsl:template match="/">
  4.        <xsl:value-of select="date:FormatDate(order/date, 'yyyy-MM-dd')"/>
  5.    </xsl:template>
  6. </xsl:stylesheet>
复制代码

在C#代码中添加扩展对象:
“`csharp
XsltArgumentList argsList = new XsltArgumentList();
argsList.AddExtensionObject(“urn:date-extensions”, new DateExtensions());

xslt.Transform(input, argsList, output);
  1. 4. **使用XSLT 2.0或更高版本(通过第三方处理器)**:
  2.    ```xml
  3.    <!-- XSLT 2.0示例 -->
  4.    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  5.                    xmlns:xs="http://www.w3.org/2001/XMLSchema"
  6.                    exclude-result-prefixes="xs">
  7.       
  8.        <xsl:template match="/">
  9.            <xsl:value-of select="format-dateTime(current-dateTime(), '[Y0001]-[M01]-[D01]')"/>
  10.        </xsl:template>
  11.    </xsl:stylesheet>
复制代码

8.5 多语言和编码问题

问题:XML数据包含多语言内容,转换后出现乱码或字符显示不正确。

解决方案:

1. 确保XML声明中指定正确的编码:<?xml version="1.0" encoding="UTF-8"?>
2. 在XSLT中指定输出编码:<xsl:output method="html" encoding="UTF-8"/>
3.
  1. 在C#代码中使用正确的编码读写文件:
  2. “`csharp
  3. // 使用UTF-8编码读取XML文件
  4. XmlReaderSettings settings = new XmlReaderSettings();
  5. using (StreamReader streamReader = new StreamReader(“input.xml”, Encoding.UTF8))
  6. using (XmlReader reader = XmlReader.Create(streamReader, settings))
  7. {
  8.    xslt.Transform(reader, “output.html”);
  9. }
复制代码

确保XML声明中指定正确的编码:
  1. <?xml version="1.0" encoding="UTF-8"?>
复制代码

在XSLT中指定输出编码:
  1. <xsl:output method="html" encoding="UTF-8"/>
复制代码

在C#代码中使用正确的编码读写文件:
“`csharp
// 使用UTF-8编码读取XML文件
XmlReaderSettings settings = new XmlReaderSettings();
using (StreamReader streamReader = new StreamReader(“input.xml”, Encoding.UTF8))
using (XmlReader reader = XmlReader.Create(streamReader, settings))
{
   xslt.Transform(reader, “output.html”);
}

// 使用UTF-8编码写入输出文件
   XmlWriterSettings writerSettings = new XmlWriterSettings();
   writerSettings.Encoding = Encoding.UTF8;
   using (XmlWriter writer = XmlWriter.Create(“output.xml”, writerSettings))
   {
  1. xslt.Transform(input, writer);
复制代码

}
  1. 4. **在HTML中指定字符集**:
  2.    ```xml
  3.    <xsl:output method="html" encoding="UTF-8"/>
  4.    
  5.    <xsl:template match="/">
  6.        <html>
  7.            <head>
  8.                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  9.                <title><xsl:value-of select="title"/></title>
  10.            </head>
  11.            <body>
  12.                <!-- 内容 -->
  13.            </body>
  14.        </html>
  15.    </xsl:template>
复制代码

1.
  1. 处理特定语言的排序和格式化:
  2. “`csharp
  3. // 设置文化信息以影响排序和格式化
  4. CultureInfo culture = new CultureInfo(“zh-CN”); // 中文-中国
  5. Thread.CurrentThread.CurrentCulture = culture;
  6. Thread.CurrentThread.CurrentUICulture = culture;
复制代码

// 执行XSLT转换
   xslt.Transform(input, argsList, output);
   “`

9. 总结与展望

9.1 XSLT在.NET框架中的价值

XSLT作为一种强大的XML转换技术,在.NET框架中得到了全面的支持。通过本文的介绍和实战案例,我们可以看到XSLT在以下方面具有重要价值:

1. 数据转换与集成:XSLT能够将不同格式的XML数据转换为系统所需的格式,实现企业应用间的数据集成。
2. 内容生成与发布:通过XSLT可以将XML数据自动转换为HTML、PDF等多种格式的文档,实现内容的自动化生成和发布。
3. 报表生成:XSLT结合扩展函数,可以生成复杂的业务报表,包括图表、汇总数据等。
4. 代码与数据分离:XSLT实现了数据处理逻辑与表示的分离,使系统更加灵活和可维护。
5. 跨平台兼容性:XSLT是一种W3C标准,具有良好的跨平台兼容性,可以在不同的系统和环境中使用。

数据转换与集成:XSLT能够将不同格式的XML数据转换为系统所需的格式,实现企业应用间的数据集成。

内容生成与发布:通过XSLT可以将XML数据自动转换为HTML、PDF等多种格式的文档,实现内容的自动化生成和发布。

报表生成:XSLT结合扩展函数,可以生成复杂的业务报表,包括图表、汇总数据等。

代码与数据分离:XSLT实现了数据处理逻辑与表示的分离,使系统更加灵活和可维护。

跨平台兼容性:XSLT是一种W3C标准,具有良好的跨平台兼容性,可以在不同的系统和环境中使用。

9.2 最佳实践总结

在使用.NET框架下的XSLT技术时,以下最佳实践值得遵循:

1. 使用XslCompiledTransform:始终使用XslCompiledTransform而不是已过时的XslTransform,以获得更好的性能。
2. 缓存XSLT样式表:对于频繁使用的XSLT样式表,实现缓存机制,避免重复编译的开销。
3. 优化XSLT样式表:使用键(key)提高查询效率避免使用//进行全局搜索使用模板匹配代替for-each减少不必要的输出
4. 使用键(key)提高查询效率
5. 避免使用//进行全局搜索
6. 使用模板匹配代替for-each
7. 减少不必要的输出
8. 合理使用扩展函数:通过扩展对象扩展XSLT的功能,但要注意不要过度依赖,保持XSLT的可移植性。
9. 处理大文件时使用流式处理:对于大型XML文件,使用XmlReader和XmlWriter进行流式处理,避免内存问题。
10. 正确处理命名空间:在XSLT中正确声明和使用命名空间,确保XPath表达式能够正确匹配节点。
11. 注意字符编码:确保XML文档和XSLT样式表使用正确的字符编码,避免乱码问题。

使用XslCompiledTransform:始终使用XslCompiledTransform而不是已过时的XslTransform,以获得更好的性能。

缓存XSLT样式表:对于频繁使用的XSLT样式表,实现缓存机制,避免重复编译的开销。

优化XSLT样式表:

• 使用键(key)提高查询效率
• 避免使用//进行全局搜索
• 使用模板匹配代替for-each
• 减少不必要的输出

合理使用扩展函数:通过扩展对象扩展XSLT的功能,但要注意不要过度依赖,保持XSLT的可移植性。

处理大文件时使用流式处理:对于大型XML文件,使用XmlReader和XmlWriter进行流式处理,避免内存问题。

正确处理命名空间:在XSLT中正确声明和使用命名空间,确保XPath表达式能够正确匹配节点。

注意字符编码:确保XML文档和XSLT样式表使用正确的字符编码,避免乱码问题。

9.3 未来发展趋势

随着技术的发展,XSLT在.NET框架中的应用也在不断演进:

1. XSLT 3.0支持:虽然.NET框架目前主要支持XSLT 1.0和部分2.0功能,但未来可能会增加对XSLT 3.0的支持,提供更强大的功能。
2. 与LINQ to XML的集成:.NET中的LINQ to XML提供了另一种XML处理方式,未来可能会看到XSLT与LINQ to XML更紧密的集成。
3. 性能优化:随着.NET Core和.NET 5+的发展,XSLT处理的性能可能会进一步优化。
4. 云原生支持:随着云计算的普及,XSLT技术可能会更好地适应云原生环境,支持分布式处理和微服务架构。
5. 与其他技术的融合:XSLT可能会与JSON处理、GraphQL等新兴数据技术更好地融合,提供更全面的数据转换解决方案。

XSLT 3.0支持:虽然.NET框架目前主要支持XSLT 1.0和部分2.0功能,但未来可能会增加对XSLT 3.0的支持,提供更强大的功能。

与LINQ to XML的集成:.NET中的LINQ to XML提供了另一种XML处理方式,未来可能会看到XSLT与LINQ to XML更紧密的集成。

性能优化:随着.NET Core和.NET 5+的发展,XSLT处理的性能可能会进一步优化。

云原生支持:随着云计算的普及,XSLT技术可能会更好地适应云原生环境,支持分布式处理和微服务架构。

与其他技术的融合:XSLT可能会与JSON处理、GraphQL等新兴数据技术更好地融合,提供更全面的数据转换解决方案。

9.4 结语

XSLT作为一种成熟、标准化的XML转换技术,在.NET框架中提供了强大而灵活的数据处理能力。通过本文的介绍,我们了解了XSLT的基础概念、.NET框架中的支持、基本和高级应用技术,以及性能优化策略和实战案例。

掌握XSLT技术,能够帮助开发者更高效地处理XML数据转换任务,实现企业应用间的数据集成,自动化内容生成和发布,以及复杂报表的生成。随着技术的不断发展,XSLT在.NET框架中的应用将继续演进,为开发者提供更强大、更高效的XML数据处理能力。

希望本文能够帮助读者深入理解.NET框架下的XSLT技术,并在实际项目中灵活应用,解决各种XML数据转换与处理的挑战。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则