8

问:我正在将 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
4

5 回答 5

8

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

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

另请参阅处理空值 (ADO.NET)。,特别是解释行为的本节:

此外,以下规则适用于 DataRow.["columnName"] 空分配的实例:

1.默认默认值是 DbNull.Value ,除了强类型空列,它是适当的强类型空值。

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

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

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

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

于 2012-04-19T16:37:21.323 回答
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;
        }
于 2012-04-19T15:53:44.727 回答
2

两点:

首先: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 语句来检索它。

此外,混淆您的代码也是个好主意。

于 2012-10-29T18:23:55.030 回答
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);
}
于 2013-08-11T00:23:12.130 回答
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;
}
于 2016-03-25T07:43:18.593 回答