Question: I\'m exporting a System.Data.DataTable to XML. So far it works fine. But I want to have all the data in attributes, which works fine as well. But my problem now, i
It's a bit old thread but, maybe it can help someone:
If you are not writing big xml files frequently (it's ok for exporting settings or something similar) you can use function below, otherwise it's better to use cutom xml schema.
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);
}
}
}
}
Usage:
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);
}
Every XML attribute must be assigned a value that is enclosed in a pair of single or double quotation marks. There is no equivalent in plain text to denote a NULL value. A pair of quotation marks with no value to represent an empty string is not the same as a NULL value. Therefore, the only way to represent a NULL attribute is to omit the attribute.
This means that you will need to either set AllowDBNull
to false and assign a suitable DefaultValue
on the DataColumn, or include the schema.
Also, see Handling Null Values (ADO.NET)., particularly this section which explains the behavior:
In addition, the following rules apply for an instance of DataRow.["columnName"] null assignments:
1.The default default value is DbNull.Value for all except the strongly typed null columns where it is the appropriate strongly typed null value.
2.Null values are never written out during serialization to XML files (as in "xsi:nil").
3.All non-null values, including defaults, are always written out while serializing to XML. This is unlike XSD/XML semantics where a null value (xsi:nil) is explicit and the default value is implicit (if not present in XML, a validating parser can get it from an associated XSD schema). The opposite is true for a DataTable: a null value is implicit and the default value is explicit.
4.All missing column values for rows read from XML input are assigned NULL. Rows created using NewRow or similar methods are assigned the DataColumn's default value.
5.The IsNull method returns true for both DbNull.Value and INullable.Null.
Try setting the column DefaultValue to something valid
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;
}
Two points:
First: The ExportTable() threw an exception: "DataTable already belongs to another DataSet." When I executed:
ds.Tables.Add(dtt)
I corrected this by making a local copy of the table:
Dim dtX As DataTable = dtt.Copy
ds.Tables.Add(dtX)
ds.DataSetName = strTable
This worked well.
Second: If you use the XML to create a dynamic SQL statement, there is no need to be concerned about the columns/fields with NULL value being omitted in the XML export. Simply walk through the a attributes that are in the XML record, build the INSERT or UPDATE statement and execute a connection command. This is faster than using a DataSet.
For INSERT it has one drawback. If the primary key is created by incrementing an identity column, an ADO.Net DataSet will return it. Dynamic SQL will require a SELECT statement to retrieve it.
Also, it would be good idea to obfuscate your code.
I have been searching the whole world for a solution of writing null fields to XML using DataSet.WriteXML(). I found that following works in a performance optimized way. I have created a function for your convenience. Change your dataset tables one after the other by calling the following function and replacing the tables.
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;
}