我可以用Linq“压扁”一个XDocument吗?

时间:2012-01-17 18:57:30

标签: linq linq-to-xml

我有一个重度嵌套的XML文档,我需要将其加载到我的数据库中以进行其他处理。由于超出本讨论范围的各种原因,我需要“扁平化”。该结构向下,然后将其加载到DataTables然后我可以SQLBulkCopy它进入数据库,它将被处理。所以假设我的原始XML看起来像这样(我的嵌套更加严重,但这是基本的想法):

<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>   

我希望将其压缩成单个(尽管是多余的)表结构,其中每个属性都成为一列(即ReportId,ReportName,DepartmentId,DepartmentName,ItemId,ItemName,Detail1,Detail2,Detail3)。

所以我的问题很简单,是否可以通过简单的Linq查询来实现这一目标&#39;?在过去,我会写一些XSLT并完成它,但我很好奇Linq库是否可以完成同样的事情?

谢谢!

1 个答案:

答案 0 :(得分:3)

这是你要找的吗?

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"),
    };

如果您想将其作为DataTable,则可以使用以下扩展方法:

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;
}

像这样使用:

var table = details.CopyToDataTable();