2012-04-19 32 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; 
}