2012-04-19 90 views
8

問題:我正在將System.Data.DataTable導出到XML。 到目前爲止,它工作正常。 但我想擁有屬性中的所有數據,這也很好。 但現在我的問題,如果在一列中,所有行都是NULL,沒有寫入空屬性。 因此,如果我將XML讀回數據表,它缺少這一列...如何將所有列作爲屬性將DataTable導出到Xml?

如何強制寫入所有列,即使它們是空的?
(數據類型不necessarely字符串)

public void ExportTable(string strDirectory, DataTable dtt) 
{ 
    using (System.Data.DataSet ds = new System.Data.DataSet()) { 
     string strTable = dtt.TableName; 

     ds.Tables.Add(dtt); 
     ds.DataSetName = strTable; 

     // Move data to attributes 
     foreach (DataTable dt in ds.Tables) { 

      foreach (DataColumn dc in dt.Columns) { 
       dc.ColumnMapping = MappingType.Attribute; 
      } 

     } 

     System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings(); 
     settings.Indent = true; 
     //settings.Encoding = System.Text.Encoding.GetEncoding("ISO-8859-1") 
     settings.Encoding = System.Text.Encoding.UTF8; 
     settings.CloseOutput = true; 
     settings.CheckCharacters = true; 
     settings.NewLineChars = "\r\n"; 
     // vbCr & vbLf 

     // Write as UTF-8 with indentation 
     using (System.Xml.XmlWriter w = System.Xml.XmlWriter.Create(System.IO.Path.Combine(strDirectory, strTable + ".xml"), settings)) { 

      // Strip out timezone 
      foreach (DataTable dt in ds.Tables) { 

       foreach (DataColumn dc in dt.Columns) { 

        if (object.ReferenceEquals(dc.DataType, typeof(DateTime))) { 
         dc.DateTimeMode = DataSetDateTime.Unspecified; 
        } 

       } 

      } 

      ds.Tables[0].WriteXml(w, XmlWriteMode.IgnoreSchema); 
      w.Flush(); 
      w.Close(); 
     } 
     // w 

    } 
    // ds 

} 
// ExportTable 

VB.NET原:

Public Sub ExportTable(strDirectory As String, dtt As DataTable) 
     Using ds As New System.Data.DataSet() 
      Dim strTable As String = dtt.TableName 

      ds.Tables.Add(dtt) 
      ds.DataSetName = strTable 

      ' Move data to attributes 
      For Each dt As DataTable In ds.Tables 

       For Each dc As DataColumn In dt.Columns 
        dc.ColumnMapping = MappingType.Attribute 
       Next dc 

      Next dt 

      Dim settings As New System.Xml.XmlWriterSettings() 
      settings.Indent = True 
      'settings.Encoding = System.Text.Encoding.GetEncoding("ISO-8859-1") 
      settings.Encoding = System.Text.Encoding.UTF8 
      settings.CloseOutput = True 
      settings.CheckCharacters = True 
      settings.NewLineChars = vbCrLf ' vbCr & vbLf 

      ' Write as UTF-8 with indentation 
      Using w As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(System.IO.Path.Combine(strDirectory, strTable & ".xml"), settings) 

       ' Strip out timezone 
       For Each dt As DataTable In ds.Tables 

        For Each dc As DataColumn In dt.Columns 

         If dc.DataType Is GetType(DateTime) Then 
          dc.DateTimeMode = DataSetDateTime.Unspecified 
         End If 

        Next dc 

       Next dt 

       ds.Tables(0).WriteXml(w, XmlWriteMode.IgnoreSchema) 
       w.Flush() 
       w.Close() 
      End Using ' w 

     End Using ' ds 

    End Sub ' ExportTable 
+1

使用XmlWriteMode.WriteSchema。在包含模式的情況下,它不會爲null值添加屬性。 – JamieSee 2012-04-19 15:58:24

+0

@JamieSee:沒有模式會更好,因爲結果應該易於在任何編程語言中讀取,而不僅僅是.NET。 – 2012-04-19 16:12:25

回答

8

每個XML屬性必須賦值用一對單引號或雙引號括起來。純文本中沒有等價物來表示NULL值。一對沒有值表示空字符串的引號與NULL值不同。因此,表示NULL屬性的唯一方法是省略該屬性。

這意味着您需要將AllowDBNull設置爲false並在DataColumn上指定合適的DefaultValue,或者包含模式。

另外,請參閱Handling Null Values (ADO.NET).,特別是這個部分,其說明了其行爲:

此外,下列規則用於 的DataRow的一個實例[ 「COLUMNNAME」]空賦值:

1 .The默認缺省值是DbNull.Value所有除強類型的空欄的地方是適當的強類型 空值。

2.Null值序列化到XML文件的過程中從來沒有寫出來(如「XSI:無」)。

3.所有非空值,包括默認值,總是寫出來,而序列化到XML。這與XSD/XML語義其中 空值(XSI:無)是明確的,默認值是隱含的(如果 不存在XML,驗證解析器可以從相關的 XSD架構得到它)。對於DataTable,情況正好相反:空值爲 隱式,默認值爲顯式。

4.所有用於從XML輸入讀取的行缺失列值被分配NULL。使用NewRow或類似方法創建的行將被分配爲 DataColumn的默認值。

5. isNull方法返回兩個DbNull.Value和INullable.Null如此。

2

嘗試列DefaultValue設置的東西有效

foreach (DataTable dt in ds.Tables) { 

     foreach (DataColumn dc in dt.Columns) { 
      dc.ColumnMapping = MappingType.Attribute; 
      //If type is DataType string 
      dc.DefaultValue = String.Empty; 
     } 
2

兩點:

第一: 的ExportTable()拋出一個異常:「數據表已屬於另一個數據集。「當我執行:

ds.Tables.Add(dtt) 

我通過使表的本地副本糾正了這個:

Dim dtX As DataTable = dtt.Copy 
ds.Tables.Add(dtX) 
ds.DataSetName = strTable 

這行之有效

二: 如果使用XML來創建動態SQL語句中,不需要關心在XML導出中省略NULL值的列/字段。只需遍歷XML記錄中的屬性,構建INSERT或UPDATE語句並執行連接命令這比usi快一個數據集。

對於INSERT它有一個缺點。如果通過增加標識列來創建主鍵,則ADO.Net DataSet將返回它。動態SQL將需要一個SELECT語句來檢索它。

此外,混淆你的代碼是個好主意。

1

這有點舊線程,但也許它可以幫助別人:
如果你不是經常寫大的XML文件(可以導出設置或類似的東西),你可以使用下面的函數,否則最好使用cutom xml架構。

private static void addEmptyElementsToXML(DataSet dataSet) 
{ 
    foreach (DataTable dataTable in dataSet.Tables) 
    { 
     foreach (DataRow dataRow in dataTable.Rows) 
     { 
      for (int j = 0; j < dataRow.ItemArray.Length; j++) 
      { 
       if (dataRow.ItemArray[j] == DBNull.Value) 
        dataRow.SetField(j, string.Empty); 
      } 
     } 
    } 
} 

用法:

using(DataTable dTable = ..something..) 
using(DataSet dS = new DataSet()) 
using(XmlTextWriter xmlStream = new XmlTextWriter("FILENAME.XML", Encoding.UTF8)) 
{ 
    //set xml to be formatted so it can be easily red by human 
    xmlStream.Formatting = Formatting.Indented; 
    xmlStream.Indentation = 4; 

    //add table to dataset 
    dS.Tables.Add(dTable); 

    //call the mentioned function so it will set all DBNull values in dataset 
    //to string.Empty 
    addEmptyElementsToXML(dS); 

    //write xml to file 
    xmlStream.WriteStartDocument(); 
    dS.WriteXml(xmlStream); 
} 
0

我一直在尋找全世界編寫使用DataSet.WriteXML()空字段到XML的解決方案。我發現遵循性能優化的方式進行工作。爲了您的方便,我創建了一個函數。通過調用以下函數並替換表格,一個接一個地更改數據集表。

private DataTable GetNullFilledDataTableForXML(DataTable dtSource) 
{ 
    // Create a target table with same structure as source and fields as strings 
    // We can change the column datatype as long as there is no data loaded 
    DataTable dtTarget = dtSource.Clone(); 
    foreach (DataColumn col in dtTarget.Columns) 
     col.DataType = typeof(string); 

    // Start importing the source into target by ItemArray copying which 
    // is found to be reasonably fast for null operations. VS 2015 is reporting 
    // 500-525 milliseconds for loading 100,000 records x 10 columns 
    // after null conversion in every cell 
    // The speed may be usable in many circumstances. 
    // Machine config: i5 2nd Gen, 8 GB RAM, Windows 7 64bit, VS 2015 Update 1 
    int colCountInTarget = dtTarget.Columns.Count; 
    foreach (DataRow sourceRow in dtSource.Rows) 
    { 
     // Get a new row loaded with data from source row 
     DataRow targetRow = dtTarget.NewRow(); 
     targetRow.ItemArray = sourceRow.ItemArray; 

     // Update DBNull.Values to empty string in the new (target) row 
     // We can safely assign empty string since the target table columns 
     // are all of string type 
     for (int ctr = 0; ctr < colCountInTarget; ctr++) 
      if (targetRow[ctr] == DBNull.Value) 
       targetRow[ctr] = String.Empty; 

     // Now add the null filled row to target datatable 
     dtTarget.Rows.Add(targetRow); 
    } 

    // Return the target datatable 
    return dtTarget; 
}