Reputation: 34188
this is how my xml look like
<?xml version="1.0" encoding="utf-8"?>
<Root>
<orders>
<OrderID>10248</OrderID>
<CustomerID>VINET</CustomerID>
<EmployeeID>5</EmployeeID>
<OrderDate>1996-07-04T00:00:00</OrderDate>
<RequiredDate>1996-08-01T00:00:00</RequiredDate>
<ShippedDate>1996-07-16T00:00:00</ShippedDate>
<ShipVia>3</ShipVia>
<Freight>32.3800</Freight>
<ShipName>Vins et alcools Chevalier</ShipName>
<ShipAddress>59 rue de l'Abbaye</ShipAddress>
<ShipCity>Reims</ShipCity>
<ShipPostalCode>51100</ShipPostalCode>
<ShipCountry>France</ShipCountry>
</orders>
</Root>
i parse the above xml using LINQ this way
document.Descendants("orders").Select(c => c).ToList()
i want to list xml data in tabular format just like sql query return data.
i search and got a link http://stackoverflow.com/questions/18608959/convert-linq-query-results-to-datatable-c-sharp
the link suggested to use CopyToDataTable() extension
method which i followed but still i am not getting result in tabular format.
so guide me how could i convert my ToList() to Datatable
as a result output would look like
OrderID CustomerID EmployeeID
------- ---------- -----------
10248 VINET 5
10249 AJAY 11
11027 Smith 09
public static class DataSetLinqOperators
{
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
{
return new ObjectShredder<T>().Shred(source, null, null);
}
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source,
DataTable table, LoadOption? options)
{
return new ObjectShredder<T>().Shred(source, table, options);
}
}
public class ObjectShredder<T>
{
private FieldInfo[] _fi;
private PropertyInfo[] _pi;
private Dictionary<string, int> _ordinalMap;
private Type _type;
public ObjectShredder()
{
_type = typeof(T);
_fi = _type.GetFields();
_pi = _type.GetProperties();
_ordinalMap = new Dictionary<string, int>();
}
public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options)
{
if (typeof(T).IsPrimitive)
{
return ShredPrimitive(source, table, options);
}
if (table == null)
{
table = new DataTable(typeof(T).Name);
}
// now see if need to extend datatable base on the type T + build ordinal map
table = ExtendTable(table, typeof(T));
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator())
{
while (e.MoveNext())
{
if (options != null)
{
table.LoadDataRow(ShredObject(table, e.Current), (LoadOption)options);
}
else
{
table.LoadDataRow(ShredObject(table, e.Current), true);
}
}
}
table.EndLoadData();
return table;
}
public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options)
{
if (table == null)
{
table = new DataTable(typeof(T).Name);
}
if (!table.Columns.Contains("Value"))
{
table.Columns.Add("Value", typeof(T));
}
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator())
{
Object[] values = new object[table.Columns.Count];
while (e.MoveNext())
{
values[table.Columns["Value"].Ordinal] = e.Current;
if (options != null)
{
table.LoadDataRow(values, (LoadOption)options);
}
else
{
table.LoadDataRow(values, true);
}
}
}
table.EndLoadData();
return table;
}
public DataTable ExtendTable(DataTable table, Type type)
{
// value is type derived from T, may need to extend table.
foreach (FieldInfo f in type.GetFields())
{
if (!_ordinalMap.ContainsKey(f.Name))
{
DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
: table.Columns.Add(f.Name, f.FieldType);
_ordinalMap.Add(f.Name, dc.Ordinal);
}
}
foreach (PropertyInfo p in type.GetProperties())
{
if (!_ordinalMap.ContainsKey(p.Name))
{
DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
: table.Columns.Add(p.Name, p.PropertyType);
_ordinalMap.Add(p.Name, dc.Ordinal);
}
}
return table;
}
public object[] ShredObject(DataTable table, T instance)
{
FieldInfo[] fi = _fi;
PropertyInfo[] pi = _pi;
if (instance.GetType() != typeof(T))
{
ExtendTable(table, instance.GetType());
fi = instance.GetType().GetFields();
pi = instance.GetType().GetProperties();
}
Object[] values = new object[table.Columns.Count];
foreach (FieldInfo f in fi)
{
values[_ordinalMap[f.Name]] = f.GetValue(instance);
}
foreach (PropertyInfo p in pi)
{
values[_ordinalMap[p.Name]] = p.GetValue(instance, null);
}
return values;
}
}
and then call this way DataTable dtt =document.Descendants("orders").Select(c => c).ToList().CopyToDataTable();
but it did not worked the way i want the output.
Upvotes: 0
Views: 1540
Reputation: 31723
Microsoft release a sample class which works pretty reliable and fast (fast in terms of as fast as a reflection based solution can be).
It's called ObjectShredder
and you can grab it from msdn
Works best with this ExtensionMethod
public static class CustomLINQtoDataSetMethods
{
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
{
return new ObjectShredder<T>().Shred(source, null, null);
}
public static DataTable CopyToDataTable<T>(
this IEnumerable<T> source, DataTable table, LoadOption? options)
{
return new ObjectShredder<T>().Shred(source, table, options);
}
}
But since document.Descendants("orders")
returns XElement
types which don't have usefull properties but some XElements I would suggest you use an anonymous type instead.
Usage:
var query = from o in document.Descendants("orders")
select new
{
OrderID = (int)o.Element("OrderID"),
CustomerID = (string)o.Element("CustomerID"),
EmployeeID = (int)o.Element("EmployeeID"),
};
var table = query.ToDataTable();
Upvotes: 1
Reputation: 218
I can't add comment yet, so here what you can do
based on the answer in the following thread
you need to reference the FastMember
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
if (data == null)
throw new ArgumentNullException("data");
var table = new DataTable("sd");
using (var reader = ObjectReader.Create(data))
{
table.Load(reader);
}
return table;
}
and if you are working with DataContext ( linq-to-sql)
public static DataTable ToDataTable<T>(this IQueryable<T> query, DataContext context)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
IDbCommand cmd = context.GetCommand(query.AsQueryable());
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = (SqlCommand)cmd;
DataTable dt = new DataTable("sd");
try
{
cmd.Connection.Open();
adapter.FillSchema(dt, SchemaType.Source);
adapter.Fill(dt);
}
finally
{
cmd.Connection.Close();
}
return dt;
}
UPDATED
you can use the following function
public static DataTable ConvertToDataTable(IEnumerable<XElement> data)
{
var table = new DataTable();
// create the columns
foreach(var xe in data.First().Descendants())
table.Columns.Add(xe.Name.LocalName,typeof(string));
// fill the data
foreach(var item in data)
{
var row = table.NewRow();
foreach(var xe in item.Descendants())
row[xe.Name.LocalName] = xe.Value;
table.Rows.Add(row);
}
return table;
}
and to use the function
var table = ConvertToDataTable(document.Descendants("orders"));
here working sample
Upvotes: 1
Reputation: 900
Use readxml to convert xml to data tabvle. eg Below ,values is of type IEnumerable
IEnumerable<XElement> values =
from el in root.Elements("orders")
select el;
DataSet ds = new DataSet();
ds.ReadXml(new StringReader(new XElement("Root", values).ToString()));
DataTable d = ds.Tables[0];
dgv.DataSource = d; // dgv is a Data Grid View
Upvotes: 0