文章出處

      關于XML,之前解析過電文收發方面的,就是所謂的帶表頭、前綴(命名空間)SOAP格式。這次需求是解析一個xml的模板(xls內容),然后填充數據,最后保存。需要時可轉換xls、pdf等文件。關于這種帶屬性xml文件, 只要了解linq操作的方法, 在參照微軟官網上有樣例,XAttribute類就是定義屬性。基本上多數都可以解決。詳細方法,隨筆XML分類中,介紹的比較詳細,至于今天要介紹的xls內容的xml文件的解析,以及創建節點也不在分篇,大致介紹下整體的思路。

比如其中某個定義表節點:

  <cto:Worksheet cto:Name="工作表1">
    <cto:Table cto:ExpandedColumnCount="12" cto:ExpandedRowCount="3" x:FullColumns="1" 
    x:FullRows="1" cto:DefaultColumnWidth="54" cto:DefaultRowHeight="16.5">
      <cto:Column cto:Width="69.75" />
      <cto:Column cto:AutoFitWidth="0" cto:Width="84" />
      <cto:Column cto:AutoFitWidth="0" cto:Width="66" />
      <cto:Column cto:Width="69.75" />
      <cto:Column cto:Index="6" cto:Width="69.75" cto:Span="1" />
      <cto:Column cto:Index="8" cto:Width="57" />
      <cto:Column cto:Width="69.75" />
      <cto:Column cto:Width="71.25" />
      <cto:Row cto:Height="17.25">
        <cto:Cell cto:StyleID="s19"><cto:Data cto:Type="String">欄位1</cto:Data></cto:Cell>
        <cto:Cell cto:StyleID="s18"><cto:Data cto:Type="String">欄位2</cto:Data></cto:Cell>
        <cto:Cell cto:StyleID="s18"><cto:Data cto:Type="String">欄位3</cto:Data></cto:Cell>
        <cto:Cell cto:StyleID="s18"><cto:Data cto:Type="String">欄位4</cto:Data></cto:Cell>
        <cto:Cell cto:StyleID="s18"><cto:Data cto:Type="String">欄位5</cto:Data></cto:Cell>
      </cto:Row>
      <cto:Row>
        <cto:Cell><cto:Data cto:Type="String">1001</cto:Data></cto:Cell>
        <cto:Cell><cto:Data cto:Type="String">1002</cto:Data></cto:Cell>
        <cto:Cell><cto:Data cto:Type="String">1003</cto:Data></cto:Cell>
        <cto:Cell><cto:Data cto:Type="String">1004</cto:Data></cto:Cell>
        <cto:Cell><cto:Data cto:Type="String">1005</cto:Data></cto:Cell>
      </cto:Row>
    </cto:Table>

這種xls類型的xml,用Linq來解析,和之前的Soap格式的并無二致,均是XElement 加載,命名空間+節點名 來獲取集合,在通過欄位值、屬性值等來獲取到某個指定的節點,最后進行設置。

如想讓欄位1 的值改成 列名1,操作如下

          XNamespace ss = @"xxxx...";
          //獲取待賦值集合
          var setcode = from d in xml.Descendants(cto + "Data")
                select d;
          //給固定表頭賦值                
          foreach (var item in setcode.ToList())
          {
             if (item.Value == "欄位1")
             {
                    item.SetValue("列名1");
             }
          }

可見并沒有任何質的改變,也就是所謂 命名空間+節點 格式,除此之外,該xml更多的定義了表的規范,長度,寬度,欄位行數、列數、節點屬性(不可小看,后面會用到)之類的,看的人眼花繚亂。這種格式的xml,解析之后當然是往其中填充數據,比如從某表中導出某條件下的多筆數據,將其填充至此xml中。

       仔細分析,該xml的表節點定義了一行列名,一行預覽值,一般分兩種情況:如果是單筆數據,那就是將預覽行的值替換為表中撈取的數據;如果是多筆,從第三行開始,循環創建節點并賦值,最后將其節點插入預覽行同級節點的下一個節點。其實沒必要這樣,直接循環組一個節點并賦值,然后插入預覽行下放,最后刪除預覽行即可。

(ps:row 為讀取DataTable行數)

                //創建節點              
                XElement addElement = null;
                //填充節點
                for (int i = 0; i < row; i++)
                {
                    addElement = new XElement(cto+ "Row", 
                        new XElement(cto + "Cell",
                             new XAttribute(cto + "Index", "2"),
                            new XElement(cto + "Data", new XAttribute(cto+ "Type", "String"), dt.Rows[i][0])),
                        new XElement(cto + "Cell",
                            new XElement(cto+ "Data", new XAttribute(cto+ "Type", "String"), dt.Rows[i][1])),
                        new XElement(cto+ "Cell",
                            new XElement(cto+ "Data", new XAttribute(cto+ "Type", "String"), dt.Rows[i][2])),
                        new XElement(cto+ "Cell",
                            new XElement(ss + "Data", new XAttribute(cto+ "Type", "String"), dt.Rows[i][3])),
                        new XElement(cto+ "Cell",
                            new XElement(cto+ "Data", new XAttribute(cto+ "Type", "String"), dt.Rows[i][4]))
                    );
                    //插入數據
                    set.FirstOrDefault().AddAfterSelf(addElement);
                }
                //刪除預覽行
                set.Remove();
                //保存文件                                                         
                xml.Save(newxmlPath);

寫到這里,本以為XML已填充完成,實則不然,這里我也是踩到坑,后來將其xml轉xls的時候,一直包失敗,無法打開調用時的excle。網上有的說是WPS的鍋,后來卸載,然后安裝Office 又遇到了同樣的錯誤。后來小組長中終于找到了其中的原因,是XML屬性的鍋!!! 前面提到xml定義表格的時候,指明了行數,cto:ExpandedRowCount=“3” ,這樣當插入節點中的行數超過了這個設置值時,打開excle就會出錯。問題原因就在這里。所以在插入節點之后保存xml之前,一定要重新設置其這個值,前面的方法已經介紹過如何設置值,這里就不在多說。

       當將xml轉換為xls格式的時候,方法有很多中,nopi 實際上是創建一個xls,然后按照一行一列的賦值,設置樣式最后保存文件。對于本就是xls內容的xml使用nopi方法是十分愚蠢也是不可行的。總不能挨個解析其中xml的每個節點值和屬性值,然后挨個在xls中賦值。其次還有 spire方式,這種方法是先去官網注冊然后下載,安裝,最后將安裝文件中的dll添加至項目引用中,功能和強大,需要轉xls就引用xls的dll,需要轉pdf就引用pdf的dll 等等,代碼也只需要三行,簡潔又方便,CSDN中不少博主立推這種方法,我想說的是,這是一個坑,沒錯,就是一個坑,因為spire插件轉xls 只支持200行,如果想要更多的話需要買會員!!!而且轉PDF的時候,左下角會有水印之類的spire的標簽以及官網地址...

對于此類Xml格式,以上兩種均不可行,然后就剩下第三種,引用 Microsoft.Office.Interop.Excel 插件,寫幾行代碼,原理大致就是,用本地的excle打開需要轉換的xml,然后保存成xls格式。

不少園主對這種方法都有說明介紹,代碼量也很少,這里就不貼了。畢竟轉換格式這塊我也是復制別人的代碼。。。

      對于轉pdf格式,轉換之后是分頁的,比較納悶,好好的一個表格,從中間切開,這種問題某度上面也是說法不一,當然是excle表長度的問題,不得不說還是小組長厲害,在茫茫xml中定義電子表格的時候,添加一個節點,設置紙張方向為橫向 <Layout x:Orientation="Landscape"/>,在調節下邊距大小即可滿足pdf的標準產出。

謹以此文記錄操作xls內容的xml文件時的一些方法以及坑

 

---市人皆大笑,舉手揶揄之


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()