首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# XSLT 代碼生成器實戰(下):模板引擎深度解析與多模板擴展

     編輯:關於C#

前言:從資料結構到程式碼的自動化躍遷

承接上講,本文深入剖析 XSLT 模板如何將資料庫結構 XML 轉換為 C#、Java、VB、HTML 等多種目標代碼。我們將逐行拆解核心模板 _csharphashtable.xslt,並展示 _HTML.xslt 如何實現單表/全庫雙模式輸出,最後說明熱插拔式模板擴展機制。

XML 序列化:物件樹到 XML 文檔

ASPX 頁面 GetXMLString 使用 XmlSerializer 將 DataBaseInfo → TableInfo → FieldInfo 三層物件樹序列化為 XML。關鍵細節:

  • 公開可讀寫屬性/欄位自動映射為 XML 元素/屬性
  • 物件樹遞迴序列化,形成多層巢狀結構
  • [XmlType("Table")] 屬性將類別名 TableInfo 映射為 XML 元素 <Table>
  • 為支援序列化,FieldInfo 的布林屬性(IsString 等)需提供 set 存取子

產出的 XML 片段(以 Customers 表為例):

<Table>
  <Name>Customers</Name>
  <Fields>
    <Field>
      <Name>Address</Name>
      <Remark>地址</Remark>
      <FieldType>Char</FieldType>
      <IsString>true</IsString>
      <ValueTypeName>System.String</ValueTypeName>
      <FieldWidth>60</FieldWidth>
      <Nullable>true</Nullable>
      <PrimaryKey>false</PrimaryKey>
    </Field>
    ...
  </Fields>
</Table>

核心模板 _csharphashtable.xslt 逐節解析

此模板生成以 Hashtable 內部儲存欄位值的 C# 實體類別。

1. 模板入口與參數驗證

<xsl:template match="/*">
  <xsl:if test="name(.) != 'Table'">
    <font color="red">本模板只能用於單表</font><br/>
  </xsl:if>
  <textarea ...> ... </textarea>
</xsl:template>
  • match="/*" 匹配 XML 根節點
  • name(.) 檢查根元素名稱,非 Table 則輸出警告(XSLT 無 return,後續仍繼續輸出)
  • 使用 <textarea> 包裹輸出,避免 HTML 特殊字元逸出麻煩

2. 變數與字串拼接

<xsl:variable name="classname">
  <xsl:value-of select="concat('DB2_' , Name )"/>
</xsl:variable>
  • XSLT 變數實為常數,賦值後不可變
  • concat() 為標準字串連接函數,參數 ≥ 2,支援 XPath 路徑與字面常數混合

3. 靜態屬性與欄位名稱陣列生成

<xsl:text>
public static string[] FieldNames { get { return new string[] { </xsl:text>
<xsl:for-each select="Fields/Field">
  <xsl:if test="position() > 1"><xsl:text>,</xsl:text></xsl:if>
  <xsl:text>"</xsl:text><xsl:value-of select="Name"/><xsl:text>"</xsl:text>
</xsl:for-each>
<xsl:text> }; } }</xsl:text>
  • position() 返回當前節點在節點集中的 1-based 索引,用於控制逗號輸出
  • xsl:text 保留空白字元(含換行),提升輸出代碼可讀性

4. 欄位屬性生成:條件分支與型別轉換

<xsl:for-each select="Fields/Field">
  <xsl:variable name="remark">...</xsl:variable>
  <!-- 註解與屬性宣告 -->
  public <xsl:value-of select="concat(ValueTypeName, ' ', Name)"/> {
    get { return <xsl:choose>
      <xsl:when test="ValueTypeName='System.Byte[]'">
        (<xsl:value-of select="ValueTypeName"/>) myValues["<xsl:value-of select="Name"/>"]
      </xsl:when>
      <xsl:otherwise>
        Convert.To<xsl:value-of select="substring-after(ValueTypeName, '.')"/>(myValues["<xsl:value-of select="Name"/>"])
      </xsl:otherwise>
    </xsl:choose>; }
    set { myValues["<xsl:value-of select="Name"/>"] = value; }
  }
</xsl:for-each>
  • xsl:choose/when/otherwise 類似 switch/case/default,但 when 可寫任意 XPath 條件,更靈活
  • substring-after(ValueTypeName, '.') 從 System.String 取得 String,構造 Convert.ToString(...)
  • 位元組陣列特殊處理:直接強制轉型,避免 Convert.ToByte[] 不存在問題

多模板協作:_HTML.xslt 單表/全庫雙模式

此模板展示命名模板重用技巧:

<xsl:template match="/*">
  <xsl:for-each select="//Table">  <!-- XPath // 選取所有後代 Table -->
    <xsl:call-template name="table"/>
  </xsl:for-each>
</xsl:template>

<xsl:template name="table">
  <div>...表頭資訊...</div>
  <table>
    <tr bgcolor="#eeee99">...標題列...</tr>
    <xsl:for-each select="Fields/Field">
      <xsl:sort select="Name"/>  <!-- 依欄位名排序 -->
      <tr>
        <xsl:attribute name="bgcolor">
          <xsl:if test="position() mod 2 = 0">#eeeeee</xsl:if>
        </xsl:attribute>
        <td><font><xsl:if test="PrimaryKey='true'"><xsl:attribute name="color">red</xsl:attribute></xsl:if><xsl:value-of select="Name"/></font></td>
        ...
      </tr>
    </xsl:for-each>
  </table>
</xsl:template>

設計精華:

  • 主模板 match="/*" 遍歷所有 //Table,呼叫子模板 table → 單一模板檔支援單表與全庫輸出
  • xsl:attribute 動態生成屬性:隔行變色(position() mod 2)、主鍵紅字
  • xsl:sort 確保輸出穩定順序

執行引擎:ASP.NET 中的 XSLT 轉換管線

// cmdCreate_Click 事件處理核心
DataBaseInfo info = GetInfo();
string xml = (cboTable.SelectedIndex == 0) ? GetXMLString(info) : GetXMLString(selectedTable);

if (cboXSLT.SelectedIndex <= 0) {
    // 直接顯示 XML
    html = "<textarea>" + xml + "</textarea>";
} else {
    // XSLT 轉換
    XslTransform transform = new XslTransform();
    transform.Load(Server.MapPath(cboXSLT.SelectedValue) + ".xslt");
    StringWriter writer = new StringWriter();
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);
    transform.Transform(doc, null, writer, null);
    html = writer.ToString();
}
lblResult.Text = "<b>共生成 " + html.Length + " 個字元</b><br/>" + html;
  • XslTransform 為 .NET Framework 1.x/2.0 核心轉換類(.NET Core 建議用 XslCompiledTransform)
  • Load 可接受檔案路徑、URL、XmlReader
  • 輸出目標為 StringWriter,最終取得 HTML 字串直接注入 Label

熱插拔模板擴展機制

  1. 約定:模板檔名以下劃線開頭(_*.xslt),置於應用程式目錄
  2. RefreshSystem 啟動時掃描目錄 Directory.GetFiles("_*.xslt") 填充下拉選單
  3. 使用者新增模板後點擊「重新整理系統」即可生效,無需重新編譯部署
  4. 核心邏輯與模板完全解耦,新增目標語言(如 Python、Go)只需撰寫對應 XSLT

本講小結與架構反思

本專案展示了資料驅動代碼生成的經典架構:

資料庫結構 → XML 序列化 → XSLT 模板 → 目標代碼 (C#/Java/HTML/SQL...)

優勢: 核心代碼極少(< 500 行)、模板靈活可擴展、跨平台(XSLT 為 W3C 標準,易移植至 Java/PHP)。
挑戰: XSLT 模板編寫門檻高(需同時掌握輸入 XML 結構、輸出代碼語法、XPath/XSLT 語法)、除錯工具相對匱乏。

建議:嘗試為專案新增 _python.xslt 或 _sqlproc.xslt,體驗模板驅動開發的擴展威力。下一階段我們將進入 C# 圖形開發領域,探索完全不同的技術維度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved