Can I 'flatten' an XDocument with Linq?

浪尽此生 提交于 2019-12-24 10:16:57

问题


I have a heavily nested XML document that I need to load into my db for additional processing. For various reasons beyond the scope of this discussion I need to 'flatten' that structure down, then load it into a DataTables and then I can SQLBulkCopy it into the db where it will get processed. So assume my original XML looks something like this (mine is even more heavily nested, but this is the basic idea):

<data>
    <report id="1234" name="XYZ">
        <department id="234" name="Accounting">
            <item id="ABCD" name="some item">
                <detail id="detail1" value="1"/>                    
                <detail id="detail2" value="2"/>                    
                <detail id="detail3" value="3"/>                    
            </item>
        </department>      
    </report>
 </data>   

and I want to flatten that down into a single (albeit redundant) table structure where each attribute becomes a column (i.e. ReportId, ReportName, DepartmentId, DepartmentName, ItemId, ItemName, Detail1, Detail2, Detail3).

So my question is simply 'is it possible to accomplish this with a simple Linq query'? In the past I would just write some XSLT and be done with it but I'm curious if the Linq library can accomplish the same thing?

thanks!


回答1:


Is this what you're looking for?

var doc = XDocument.Load(fileName);
var details =
    from report in doc.Root.Elements("report")
    from department in report.Elements("department")
    from item in department.Elements("item")
    from detail in item.Elements("detail")
    select new
    {
        ReportId = (int)report.Attribute("id"),
        ReportName = (string)report.Attribute("name"),
        DepartmentId = (int)department.Attribute("id"),
        DepartmentName = (string)department.Attribute("name"),
        ItemId = (string)item.Attribute("id"),
        ItemName = (string)item.Attribute("name"),
        DetailId = (string)detail.Attribute("id"),
        DetailValue = (int)detail.Attribute("value"),
    };

If you want it as a DataTable, you can use the following extension method:

public static DataTable ToDataTable<T>(this IEnumerable<T> source)
{
    PropertyInfo[] properties = typeof(T).GetProperties()
                                         .Where(p => p.CanRead && !p.GetIndexParameters().Any())
                                         .ToArray();

    DataTable table = new DataTable();
    foreach (var p in properties)
    {
        Type type = p.PropertyType;
        bool allowNull = !type.IsValueType;
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            allowNull = true;
            type = Nullable.GetUnderlyingType(type);
        }
        DataColumn column = table.Columns.Add(p.Name, type);
        column.AllowDBNull = allowNull;
        column.ReadOnly = !p.CanWrite;
    }

    foreach (var item in source)
    {
        DataRow row = table.NewRow();
        foreach (var p in properties)
        {
            object value = p.GetValue(item, null) ?? DBNull.Value;
            row[p.Name] = value;
        }
        table.Rows.Add(row);
    }

    return table;
}

Use it like this:

var table = details.CopyToDataTable();


来源:https://stackoverflow.com/questions/8900089/can-i-flatten-an-xdocument-with-linq

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!