2012-01-17 4 views
1

У меня есть сильно вложенный XML-документ, который мне нужно загрузить в мой db для дополнительной обработки. По разным причинам, выходящим за рамки этой дискуссии, мне нужно «свернуть» эту структуру вниз, а затем загрузить ее в DataTables, а затем я могу SQLBulkCopy ее в db, где она будет обработана. Так предположим, мой оригинальный XML выглядит примерно так (шахта еще более сильно вложенной, но это основная идея):Могу ли я «сгладить» XDocument с Linq?

<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»? Раньше я просто писал XSLT и делал с ним, но мне любопытно, может ли библиотека Linq выполнить одно и то же?

спасибо!

+1

Было бы реально помочь, если вы хотите показать что ваш образец ввода будет выглядеть после преобразования. Это не совсем понятно из вашего описания. –

ответ

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(); 
+0

+1 Был готов опубликовать нечто подобное, хотя ваш ответ использует меньше промежуточных шагов, чем мой подход. – Yuck

+0

Это так просто и элегантно, что я похлопываю себя за то, что не думал об этом, спасибо за вашу помощь. Для кода, который вы разместили, требуется небольшое изменение ... поскольку есть узлы 1-to-many , и мне нужно сопоставить их в зависимости от его атрибута 'id' (например, нужно сопоставить столбцу Detail1. Я получил его для работы с использованием другого запроса, подобного этому: (от a detail.Elements (" detail ") где (строка) a.Attribute (" id ") == «detail1» выберите a.Attribute («значение»). Значение) .Single(), но, вероятно, есть более простой способ – snappymcsnap

Смежные вопросы