【问题标题】:How to export a DataTable to Xml with ALL columns as attributes?如何将所有列作为属性的 DataTable 导出到 Xml?
【发布时间】:2012-04-19 15:36:07
【问题描述】:

问题:我正在将 System.Data.DataTable 导出到 XML。 到目前为止,它工作正常。 但我想拥有属性中的所有数据,这也很好。 但我现在的问题是,如果在一列中,所有行都是 NULL,则不会写入空属性。 因此,如果我将 XML 读回 DataTable,则缺少此列...

如何强制写入所有列,即使它们是空的?
(DataType 不一定是字符串)

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

【问题讨论】:

  • 使用 XmlWriteMode.WriteSchema。包含架构后,它不会为空值添加属性。
  • @JamieSee:没有模式会更好,因为结果应该在任何编程语言中都易于阅读,而不仅仅是 .NET。

标签: c# .net xml vb.net datatable


【解决方案1】:

必须为每个 XML 属性分配一个值,该值用一对单引号或双引号括起来。纯文本中没有等价物来表示 NULL 值。一对没有值的引号表示空字符串与NULL值不同。因此,表示 NULL 属性的唯一方法是省略该属性。

这意味着您需要将 AllowDBNull 设置为 false 并在 DataColumn 上分配合适的 DefaultValue,或者包含架构。

另外,请参阅Handling Null Values (ADO.NET).,尤其是解释行为的本节:

此外,以下规则适用于 DataRow.["columnName"] 空赋值:

1.默认的默认值是 DbNull.Value 对于除了强类型空列之外的所有列,它是适当的强类型 空值。

2.Null 值在序列化为 XML 文件期间永远不会写出(如“xsi:nil”)。

3.所有非空值,包括默认值,在序列化为 XML 时总是被写出。这与 XSD/XML 语义不同,其中一个 空值 (xsi:nil) 是显式的,默认值是隐式的(如果 XML 中不存在,验证解析器可以从关联的 XSD 架构)。 DataTable 则相反:空值是 隐式的,默认值是显式的。

4.从 XML 输入读取的行的所有缺失列值都分配为 NULL。使用 NewRow 或类似方法创建的行被分配 DataColumn 的默认值。

5.IsNull 方法对 DbNull.Value 和 INullable.Null 都返回 true。

【讨论】:

    【解决方案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;
            }
    

    【讨论】:

      【解决方案3】:

      两点:

      首先: ExportTable() 抛出异常:“DataTable 已经属于另一个 DataSet。”当我执行时:

      ds.Tables.Add(dtt)
      

      我通过制作表格的本地副本来纠正这个问题:

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

      效果很好。

      第二: 如果您使用 XML 创建动态 SQL 语句,则无需担心在 XML 导出中省略了具有 NULL 值的列/字段。只需遍历 XML 记录中的 a 属性,构建 INSERT 或 UPDATE 语句并执行连接命令。这比使用 DataSet 更快。

      对于 INSERT,它有一个缺点。如果通过递增标识列创建主键,ADO.Net 数据集将返回它。动态 SQL 将需要一个 SELECT 语句来检索它。

      另外,混淆你的代码也是个好主意。

      【讨论】:

        【解决方案4】:

        这是一个有点旧的线程,但也许它可以帮助某人:
        如果你不经常写大的 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);
        }
        

        【讨论】:

          【解决方案5】:

          我一直在寻找使用 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;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-12-31
            • 1970-01-01
            • 2021-10-24
            • 1970-01-01
            • 2017-10-13
            • 2010-11-15
            • 1970-01-01
            相关资源
            最近更新 更多