[Word][Solution] 利用XSD配合XSLT產出特定格式Word檔案

利用XSD設計Word版面, 再將資料(XML)搭配XSLT產出特定格式Word檔案

利用類別產生XSD檔

產出XSD檔的目的在於提供Word樣板設計之資料框架

在此使用微軟提供之XML Schema Definition Tool (Xsd.exe)工具產生XSD檔

1. 定義類別

// 書籍資料
public class Book
{
    public string BookId { get; set; }
    public string Name { get; set; }
    public string price { get; set; }
}

// 訂單資料
public class Order
{
    public string BuyerName { get; set; }
    public List<Book> Books { get; set; }
    public int TotalPrice { get; set; }
}

 

2. 使用Xsd.exe工具產生XSD檔案

   工具位置: C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools

   工具語法: xsd D:\Projects\WordGenerator.exe /language:CS /outputdir:d:\Projects\Xsd /type:Order

 

3. XSD內容如下

<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Order" nillable="true" type="Order" />
  <xs:complexType name="Order">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="BuyerName" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" name="Books" type="ArrayOfBook" />
      <xs:element minOccurs="1" maxOccurs="1" name="TotalPrice" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="ArrayOfBook">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="unbounded" name="Book" nillable="true" type="Book" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="Book">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="BookId" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" name="price" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

 

4. 加上Namespace

   targetNamespace=http://schemas.chris.com/WordGenerator/Order.xsd

   xmlns=http://schemas.chris.com/WordGenerator/Order.xsd

   image

 

利用XSD檔產生特定格式之Word檔

筆者是以Word 2010進行實作,相關實作畫面如下。另外,若使用Word 2013的朋友是無法透過此方法自行定義XML標記的,因為移除自訂 XML 標記是2009年12月22日美國法院的判決結果,購買或取得 Word 2013 授權的客戶會發現此軟體不含特定的自訂 XML 標記實作;所以請使用非Word2013版本來實作此步驟。

http://support.microsoft.com/kb/2761189/zh-tw

 

1. 加入開發人員工具列

   image

2. 點選開發人員工具列之結構描述

   image

3. 選擇剛產生之XSD檔案

   image

   image

   image

4. 開始編輯畫面與資料關聯

   目前希望呈現的樣式(黃色是隨資料異動部分)

   image

   點選結構後,產生XML結構工具

   imageimage

   圈選Order對應部分後,點選Order

   image

   此時會出現選取範圍,就選擇僅套用至選取範圍即可

   image

   此時就完成了第一次的資料對應關係

  image

  然後依序處理其他部分,結果如下

  image

  此時若發現有錯誤發生

  image

   調整一下XML選項設定即可

   image

   image

   調整完畢後儲存為DOC檔案供下次修改使用

 

利用特定格式之Word檔產生XSLT檔

1. 下載安裝 Word 2003: XML SDK

    http://www.microsoft.com/downloads/details.aspx?familyid=ca83cb4f-8dee-41a3-9c25-dd889aea781c&displaylang=en

 

2. 將Word檔另存為XML

   image

3. 利用WML2XSLT.exe 將 Order.xml 轉換成 Order.xslt

   image

   點選所需的namespace即可

   image

 

組合資料產生Word檔

資料 + 顯示樣式

    = 資料物件序列化(XML) + XSLT

    = 產生具有資料及指定樣式Word檔

 

private void CreateWord()
{

    string xsltLocation = @"D:\Order.xslt";
    string outputPath = @"D:\Order.doc";

    // Data
    Order order = new Order()
    {
        BuyerName = "Chris Chen",
        TotalPrice = 600,
        Books = new List<Book>()
        {
            new Book(){BookId="B001", Name="Name01", price="100"},
            new Book(){BookId="B002", Name="Name02", price="200"},
            new Book(){BookId="B003", Name="Name03", price="300"},
        }
    };

    // Data XML (一定要namespace)
    string szInputXml = Serialize(order, "http://schemas.chris.com/WordGenerator/Order.xsd");
    XmlTextReader xmlReader = new XmlTextReader(new System.IO.StringReader(szInputXml));

    // XSLT
    XmlReader xsltReader = XmlReader.Create(xsltLocation);

    // Create Word
    byte[] wordDoc = GetWord(xmlReader, xsltReader);

    // Write resulting array to HDD, show process information
    using (FileStream fs = new FileStream(outputPath, FileMode.Create))
        fs.Write(wordDoc, 0, wordDoc.Length);

    // Display resulting report in Word
    Process.Start(new ProcessStartInfo(outputPath));
}

public string Serialize(object obj, string defaultNamespace)
{
    // encoding issue: utf-16 => utf-8
    // http://blog.csdn.net/dancefire/article/details/1912345

    XmlSerializer xs = new XmlSerializer(obj.GetType(), defaultNamespace);

    MemoryStream stream = new MemoryStream();
    XmlWriterSettings setting = new XmlWriterSettings();
    setting.Encoding = new UTF8Encoding(false);
    setting.Indent = true;
   
    using (XmlWriter writer = XmlWriter.Create(stream, setting))
    { xs.Serialize(writer, obj); }

    return Encoding.UTF8.GetString(stream.ToArray());
}

public static byte[] GetWord(XmlReader xmlData, XmlReader xsltReader)
{
    XslCompiledTransform xslt = new XslCompiledTransform();
    XsltArgumentList args = new XsltArgumentList();

    using (MemoryStream swResult = new MemoryStream())
    {
        // Load XSLT to reader and perform transformation
        xslt.Load(xsltReader);
        xslt.Transform(xmlData, args, swResult);

        return swResult.ToArray();
    }
}

 

結果如同我們所預期的將資料都填入Word檔中

image

 

期望效益

1. 開發過程可集中於資料模型的設計(類似ViewModel概念)

2. Word樣式可供客戶自行調整 (不變更資料項目為前提下,僅需自動轉為XSLT檔即可)

3. 資料自動綁定至Word並產生檔案輸出

 

參考資料

http://www.codeproject.com/Articles/20287/Generating-Word-Reports-Documents

http://msdn.microsoft.com/en-us/library/x6c1kb0s(v=vs.110).aspx

http://blogs.msdn.com/b/williamcornwill/archive/2007/01/17/document-generation-using-wordml-word-2003.aspx


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !