SQL Server How to serialize a datatable in a way similar to the raw mode provided by T-SQL FOR XML clause?

csbfibhn  于 2023-05-05  发布在  其他
关注(0)|答案(2)|浏览(144)

T-SQL has a FOR XML clause that allows XML serialization from a rowset. This clause has a mode named as RAW which serializes every row using an xml element and serialize its column values as element attribute values. For example, a rowset like the one below:

Column_1 Column_2
-------- --------
one      1
two      2

when serialized using raw mode gets serialized as follows:

<row Column_1="one" Column_2="1" />
<row Column_1="two" Column_2="2" />

This serialization mode is light and useful when column data uses basic types, because it is a less verbose than PATH mode which serializes every column value using a xml element (requiring its open and close tags)

When a rowset from a System.Data.DataTable instance is serialized from C# using WriteXml methods it gets serialized as described for the PATH mode of the T-SQL FOR XML select clause. For the previous example it will result in something like

<DataSet>
    <row><Column_1>one</Column_1><Column_2>1</Column_2></row>
    <row><Column_1>two</Column_1><Column_2>2</Column_2></row>
</DataSet>

My question: is there a C# native way to serialize a System.DataTable instance in the same way that SELECT FOR XML RAW(<element_name>) does?

enxuqcxy

enxuqcxy1#

Thanks to @siggemanen comment linking to this post I learned that the right way to configure how a datatable is serialized when its native WriteXml methods are invoked is to set the ColumnMapping property. In this case to map them as attributes the value is MappingType.Attribute This code shows the behavior

using System;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
                    
public class Program
{
    public static void Main()
    {
        //Get a datatable with 3 columns and two rows for testing purpouses
        var dt = BuildDataTableWithData();

        //To prepare the datatable to serialize columns as xml attributes property ColumnMapping for every column must be set to MappingType.Attribute
        foreach(DataColumn dc in dt.Columns)
            dc.ColumnMapping = MappingType.Attribute;

        //Serializes and display result
        var serialized = Serialize(dt);
        Console.WriteLine(serialized);
    }
    
    private static DataTable BuildDataTableWithData() {
        var dt = new DataTable("table");
        (new DataSet("DataSet")).Tables.Add(dt);
        
        dt.Columns.AddRange(new DataColumn[] {
            new DataColumn("Column_1", typeof(string)),
            new DataColumn("Column_2", typeof(decimal)),
            new DataColumn("Column_3", typeof(DateTime))
        });
        
        dt.Rows.Add("one",1,DateTime.Now);
        dt.Rows.Add("two",1,DateTime.Now);
        dt.AcceptChanges();
        
        return dt;
    }
    
    static string Serialize(DataTable dt) {
        var enc = Encoding.ASCII;
        var mems = new MemoryStream();
        var xmlw = new XmlTextWriter(mems, enc);
                
        dt.WriteXml(xmlw, XmlWriteMode.IgnoreSchema);
        
        var serialized = enc.GetString(mems.ToArray());
        return serialized;
    }
    
}
luaexgnf

luaexgnf2#

Pretty simple, you can just use XElement and friends, along with LINQ.

var rootNode =
    new XElement(
        "DataSet",
        dataTable.Rows.Cast<DataRow>().Select(row =>
            new XElement(
                "row",
                dataTable.Columns.Cast<DataColumn>().Select(col =>
                    new XElement(col.ColumnName, row[col.Ordinal])
                )
            )
        )
    );

Console.WriteLine(rootNode.ToString());

dotnetfiddle

If you want to use attributes, just change new XElement(col.ColumnName, ... to

new XAttribute(col.ColumnName, row[col.Ordinal])

相关问题