2017-01-12 1 views
0

Я нашел много сообщений о сохранении XML как CSV, но моя проблема в том, что некоторые узлы в моем XML имеют меньше элементов, чем другие. Это приводит к отключению столбцов CSV, что не очень полезно. Данные XML, которые я потребляю, поступают от стороннего API.Отсутствуют элементы XML-узла C# - Попытка сохранить CSV

Вот мой код:

internal static void XmlToCsvFile(XmlDocument doc, string fileName) 
    { 
     var xml = new XDocument(); 
     xml = XDocument.Parse(doc.InnerXml); 

     StringBuilder sb = new StringBuilder(100000); 

     var xmlDocForHeaders = new XmlDocument(); 
     xmlDocForHeaders.InnerXml = xml.Root.FirstNode.ToString(); 

     var xdocForHeaders = XDocument.Parse(xmlDocForHeaders.InnerXml); 

     foreach (var element in xdocForHeaders.Elements().Elements()) 
     { 
      sb.Append($"{element.Name},"); 
     } 

     sb.Append("\n"); 

     foreach (XElement node in xml.Descendants("Table")) 
     { 
      foreach (XElement innerNode in node.Elements()) 
      { 
       foreach (XElement elements in innerNode.Elements()) 
       { 
        sb.Append($"\"{elements.Value}\","); 
       } 

       sb.Append("\n"); 
      } 

      sb.Remove(sb.Length - 1, 1); 
      sb.AppendLine(); 
     } 

     var csvOut = sb.ToString(); 
     SaveDataToCsvFile(csvOut); 
    } 

Я знаю, что не нужно конвертировать мой XmlDocument в XDocument, но это, казалось, проще в то время.

Любая помощь была бы принята с благодарностью!

Спасибо!

EDIT:

Вот пример данных XML:

<Table> 
    <Record> 
    <StoreID>43</StoreID> 
    <StoreName>30455 Juniper</StoreName> 
    <Disabled>0</Disabled> 
    <Abbreviation>UC30455</Abbreviation> 
    <ManagerEmployeeID>297</ManagerEmployeeID> 
    <ManagerCommissionable>false</ManagerCommissionable> 
    <Address>400 Green St.</Address> 
    <City>Juniper</City> 
    <StateProv>WA</StateProv> 
    <ZipPostal>47895</ZipPostal> 
    <Country>United States</Country> 
    <PhoneNumber>8056954823</PhoneNumber> 
    <FaxNumber>5085236524</FaxNumber> 
    <DistrictNameJuniper South</DistrictName> 
    <RegionName>High Hills Region</RegionName> 
    <ChannelName>West Market</ChannelName> 
    <StoreType>1/2 Tier-PP</StoreType> 
    <GLCode>5862</GLCode> 
    <SquareFootage>1100</SquareFootage> 
    <LocationCode>MX029</LocationCode> 
    <Latitude>49.5236458952</Latitude> 
    <Longitude>-192.532150000</Longitude> 
    <AddressVerified>Not Verified</AddressVerified> 
    <TimeZone>(GMT-08:00) West Time (US &amp; Canada)</TimeZone> 
    <AdjustDST>true</AdjustDST> 
    <CashPolicy>Single-Drawer</CashPolicy> 
    <MaxCashDrawer>0.0000</MaxCashDrawer> 
    <Serial_on_OE>false</Serial_on_OE> 
    <Phone_on_OE>true</Phone_on_OE> 
    <PAW_on_OE>true</PAW_on_OE> 
    <Comment_on_OE>false</Comment_on_OE> 
    <HideCustomerAddress>false</HideCustomerAddress> 
    <EmailAddress>[email protected]</EmailAddress> 
    <GeneralLocationNotes>Mon-Sat 9am-8pm, Sun 12pm-5pm</GeneralLocationNotes> 
    <SaleInvoiceComment /> 
    <BankDetails /> 
    <Taxes>UT - Sales Tax 6.6%, 1.9% E911 Fee (WA)</Taxes> 
    <Rent>0.0000</Rent> 
    <PropertyTaxes>0.0000</PropertyTaxes> 
    <InsuranceAmount>0.0000</InsuranceAmount> 
    <OtherCharges>0.0000</OtherCharges> 
    <DepositTaken>0.0000</DepositTaken> 
    <LeaseNotes /> 
    <InsuranceCompany /> 
    <LandlordNotes /> 
    <LandlordName>COMPANY-JUNIPER/60144582</LandlordName> 
    <UseLocationEmail>true</UseLocationEmail> 
    <LocationEntityID>25367</LocationEntityID> 
    </Record> 
    <Record> 
    <StoreID>107</StoreID> 
    <StoreName>9589 CLC Go Company.com</StoreName> 
    <Disabled>0</Disabled> 
    <Abbreviation>CL9589</Abbreviation> 
    <ManagerEmployeeID>853</ManagerEmployeeID> 
    <ManagerCommissionable>false</ManagerCommissionable> 
    <Address>9852 Bland Banks Blvd.</Address> 
    <City>Honneyton</City> 
    <StateProv>MI</StateProv> 
    <ZipPostal>69421</ZipPostal> 
    <Country>USA</Country> 
    <PhoneNumber>9595525214</PhoneNumber> 
    <FaxNumber>3625485236</FaxNumber> 
    <DistrictName>Recovery Services</DistrictName> 
    <RegionName>zRetail Support</RegionName> 
    <ChannelName>zRetail Support</ChannelName> 
    <StoreType>Care Center</StoreType> 
    <GLCode>0356</GLCode> 
    <SquareFootage>2163</SquareFootage> 
    <AddressVerified>Not Verified</AddressVerified> 
    <TimeZone>(GMT-07:00) Central Time (US &amp; Canada)</TimeZone> 
    <AdjustDST>true</AdjustDST> 
    <CashPolicy>Single-Drawer</CashPolicy> 
    <MaxCashDrawer>0.0000</MaxCashDrawer> 
    <Serial_on_OE>false</Serial_on_OE> 
    <Phone_on_OE>true</Phone_on_OE> 
    <PAW_on_OE>false</PAW_on_OE> 
    <Comment_on_OE>false</Comment_on_OE> 
    <HideCustomerAddress>false</HideCustomerAddress> 
    <EmailAddress>[email protected]</EmailAddress> 
    <GeneralLocationNotes /> 
    <SaleInvoiceComment /> 
    <BankDetails /> 
    <Taxes>MI - Honneyton</Taxes> 
    <Rent>0.0000</Rent> 
    <PropertyTaxes>0.0000</PropertyTaxes> 
    <InsuranceAmount>0.0000</InsuranceAmount> 
    <OtherCharges>0.0000</OtherCharges> 
    <DepositTaken>0.0000</DepositTaken> 
    <LeaseNotes /> 
    <InsuranceCompany /> 
    <LandlordNotes /> 
    <LandlordName /> 
    <UseLocationEmail>true</UseLocationEmail> 
    <LocationEntityID>25397</LocationEntityID> 
    </Record> 
</Table> 

Как вы можете видеть, первая запись содержит лат и длинные элементы, а второй нет. Это отбрасывает все при конвертации в CSV.

Еще раз спасибо!

EDIT:

Вот что в конечном итоге работает для меня на основе всех больших комментариев!

internal static string XmlToCsvFile(XmlDocument doc, string fileName) 
{ 
    var xml = XDocument.Parse(doc.InnerXml); 

    var sb = new StringBuilder(); 

    var record = new Dictionary<string, string>(); 

    foreach (var element in xml.Descendants("Record").Elements()) 
    { 
     if (!record.ContainsKey(element.Name.ToString())) 
     { 
      record.Add(element.Name.ToString(), ""); 
     } 
    } 

    foreach (var key in record.Keys) 
    { 
     sb.Append($"{key},"); 
    } 

    sb.Remove(sb.Length - 1, 1); 
    sb.AppendLine(); 

    foreach (var node in xml.Descendants("Record")) 
    { 
     foreach (var key in record.Keys.ToList()) 
     { 
      record[key] = ""; 
     } 

     foreach (var elements in node.Elements()) 
     { 
      record[elements.Name.ToString()] = elements.Value.ToString(); 
     } 

     foreach (var value in record.Values) 
     { 
      sb.Append($"\"{value}\","); 
     } 

     sb.Remove(sb.Length - 1, 1); 
     sb.AppendLine(); 
    } 

    var csvOut = sb.ToString(); 
    SaveDataToCsvFile(csvOut); 

    return csvOut; 
} 
+2

Можете ли вы предоставить образец XML? –

+0

Почему бы не создать класс для хранения данных, а затем проанализировать XML через LINQ to XML в класс, правильно обработать отсутствующие элементы и затем записать коллекцию классов в виде CSV? – Tim

+0

Или даже в анонимный тип, который затем можно обрабатывать в CSV-файле. – Tim

ответ

0

Вот краткое описание того, что я сказал в своем комментарии. Поскольку вы, кажется, делаете это одним выстрелом, вы можете разобрать XML в коллекцию анонимных типов, а затем перебрать эту коллекцию для записи CSV.

Простой пример:

var records = xml.Descedants("Record").Select(r => new 
       { 
        StoreID = (string)r.Element("StoreID"), 
        StoreName = (string)r.Element("StoreName"), 
        // other elements 
       }; 

Обратите внимание на явное преобразование в строку - это будет возвращать нулевое значение, если элемент отсутствует, что позволит решить проблему с некоторыми элементами, отсутствующих в некоторых записях изящно. В противном случае вам придется написать кучу лишнего кода, чтобы проверить отсутствие элементов, что получилось бы очень уродливым.

После того, как у вас есть коллекция анонимных типов, вы можете перебирать их выписывать CSV:

foreach(var record in records) 
{ 
    // Here you can access the individual properties in the currently selected record 
} 
+0

Спасибо! Ваш ответ заставил меня на правильном пути, но потому, что мне нужно, чтобы мои ключи были динамически созданы (заголовок CSV должен быть динамическим) в зависимости от входящих данных, я закончил использование словаря, чтобы определить, как мои данные будут выглядеть, итерации через все моих ключей узлов и добавления новых. Затем я мог бы добавить свои данные в словарь для каждого узла. Казалось, это хорошо работает, но я чувствую, что это может быть немного более грязно, чем необходимо. Любые предложения по его очистке будут очень благодарны! Взгляните на мое редактирование выше. Еще раз спасибо за вашу помощь!! – David

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