Запрос нескольких файлов XML с помощью LINQ в операторах C # - PullRequest
1 голос
/ 14 октября 2010

Я пытаюсь извлечь данные из ряда файлов XML, размещенных в Интернете, с помощью LINQ. Я работаю с LINQPad и использую операторы C #. Все файлы имеют одинаковый формат и имена элементов. Моя цель состоит в том, чтобы извлечь одинаковые элементы из каждого файла, а затем отчитаться об элементах в одной строке на файл, создавая сетку. Это в идеале было бы экспортировано в Excel. Я новичок в LINQ, поэтому любая помощь будет принята с благодарностью. Ниже мой рабочий код:

// Load From Website.
        XElement Tags=XElement.Load("http://fapt.efanniemae.com/epooltalk-hvd/pool.xml?type=XML&pn="+"510299"+".XML");
        //XElement Tags=XElement.Load("http://fapt.efanniemae.com/epooltalk-hvd/pool.xml?type=XML&pn="+(list1)+".XML");
        XNamespace p = "http://fapt.efanniemae.com";

/Run Export

        var titles =
        from book in Tags.Descendants(p + "Pool")
        let bookAttributes = book.Element(p + "PoolFactors")
        let title = ((string)book.Element(p + "PoolNumber"))
        let title2 = ((string)bookAttributes.Element(p + "PoolFactor"))
        let month = (string)bookAttributes.Element (p + "Month")
        group title by month;

        foreach (var group in titles) {

        foreach (var title in group) {
        Console.WriteLine("Pool Num |" + title);
}
}
        foreach(XElement CusipElement in Tags.Descendants(p + "CUSIP")) {
        Console.WriteLine("CUSIP |" +(string)CusipElement);
        }
        foreach(XElement PrefixElement in Tags.Descendants(p + "PoolPrefix")) {
        Console.WriteLine("PoolPrefix |" +(string)PrefixElement);
        }
        foreach(XElement ObalElement in Tags.Descendants(p + "OriginalSecurityBalance")) {
        Console.WriteLine("Orig. Bal |" +(string)ObalElement);
        }
        foreach(XElement OtermElement in Tags.Descendants(p + "WeightedAverageOrigLoanTerm")) {
        Console.WriteLine("Orig. Term |" +(string)OtermElement);
        }
        foreach(XElement RtermElement in Tags.Descendants(p + "WAMnthsRemainingToAmortization")) {
        Console.WriteLine("Remain Term |" +(string)RtermElement);
        }
        foreach(XElement WalaElement in Tags.Descendants(p + "WeightedAverageLoanAge")) {
        Console.WriteLine("WALA |" +(string)WalaElement);
        }
        foreach(XElement AccrateElement in Tags.Descendants(p + "CurrentAccrualRate")) {
        Console.WriteLine("Net Rate |" +(string)AccrateElement);
        }           
        foreach(XElement MarginElement in Tags.Descendants(p + "WeightedAverageLoanMarginRate")) {
        Console.WriteLine("WA Margin |" +(string)MarginElement);
        }
        foreach(XElement SubtElement in Tags.Descendants(p + "SubType")) {
        Console.WriteLine("SubType |" +(string)SubtElement);
        }
        //foreach(XElement MonthElement in Tags.Descendants(p + "Month"))
        //foreach(XElement WacElement in Tags.Descendants(p + "WAC")) {
        //Console.WriteLine("WAC |" +(string)WacElement + "|" +(string)MonthElement);
        //}
        foreach(XElement UpdatedcapElement in Tags.Descendants(p + "UpdatedCap")) {
        Console.WriteLine("Updated CAP |" +(string)UpdatedcapElement);
        }
        foreach(XElement IdateElement in Tags.Descendants(p + "IssueDate")) {
        Console.WriteLine("Issue Date |" +(string)IdateElement);
        }
        foreach(XElement MdateElement in Tags.Descendants(p + "MaturityDate")) {
        Console.WriteLine("Maturity Date |" +(string)MdateElement);
        }
        foreach(XElement RadjElement in Tags.Descendants(p + "RateAdjustmentFrequency")) {
        Console.WriteLine("Rate Adj Freq |" +(string)RadjElement);
        }
        foreach(XElement PcapElement in Tags.Descendants(p + "PerAdjustmentCap")) {
        Console.WriteLine("Period Cap |" +(string)PcapElement);
        }
        foreach(XElement PchgfreqElement in Tags.Descendants(p + "PaymentChangeFrequency")) {
        Console.WriteLine("Pymt Chg Freq |" +(string)PchgfreqElement);
        }
        foreach(XElement MtrElement in Tags.Descendants(p + "WeightedAverageMonthsToRoll")) {
        Console.WriteLine("WA MTR |" +(string)MtrElement);
        }
        foreach(XElement RatecapElement in Tags.Descendants(p + "WeightedAverageCap")) {
        Console.WriteLine("WA CAP |" +(string)RatecapElement);
        }
var Months = Tags.Descendants(p + "Month")
        .Select(titleElement => (string)titleElement);
foreach (string title in Months) {
Console.WriteLine("Months |" + title);
}
var Wacs = Tags.Descendants(p + "WAC")
            .Select(titleElement => (string)titleElement);
foreach (string title in Wacs) {
Console.WriteLine("WAC |" + title);
}
var Wams = Tags.Descendants(p + "WAM")
            .Select(titleElement => (string)titleElement);
foreach (string title in Wams) {
Console.WriteLine("WAM |" + title);
}
var Factors = Tags.Descendants(p + "Factor")
            .Select(titleElement => (string)titleElement);
foreach (string title in Factors) {
Console.WriteLine("Factor |" + title);
}

Как сделать так, чтобы запрашиваемые элементы отображались горизонтально и с некоторым разделителем?

В настоящее время мой код работает только для 1 файла XML. Как это можно изменить в цикле для нескольких файлов? Все имена исходных файлов имеют один и тот же базовый URL, с той лишь разницей, что это конечный оператор. Есть ли способ сделать ссылку Load базовым URL-адресом, объединенным со списком переменных, который будет содержать конечный оператор?

Открыто для любых предложений.

Ответы [ 2 ]

1 голос
/ 15 февраля 2011

Я увидел этот вопрос, глядя на другой вопрос о переполнении стека, и заметил, что этот вопрос до сих пор остается без ответа. Я не знаю, является ли это проблемой для вас, но для всех, кто читает это, это один из способов выполнить запрос LINQ к нескольким источникам.

Хитрость заключается в том, чтобы использовать метод [Enumerable.Concat (TSource)] [1].

XElement tags=XElement.Load("http://fapt.efanniemae.com/epooltalk-hvd/pool.xml?type=XML&pn="+"510299"+".XML");
XElement tags2=XElement.Load("http://fapt.efanniemae.com/epooltalk-hvd/pool.xml?type=XML&pn="+(list1)+".XML");

var titles =
    from book in tags.Descendants(p + "Pool").Concat(tags2.Descendants(p + "Pool"))
    let bookAttributes = book.Element(p + "PoolFactors")
    let title = ((string)book.Element(p + "PoolNumber"))
    let title2 = ((string)bookAttributes.Element(p + "PoolFactor"))
    let month = (string)bookAttributes.Element (p + "Month")
    group title by month;

Надеюсь, это поможет вам или кому-либо еще.

[1]: http://msdn.microsoft.com/en-us/library/bb302894.aspx Метод Enumerable.Concat (TSource)

1 голос
/ 14 октября 2010

Похоже, вы приложили к этому достойные усилия, поэтому снимаю шляпу перед вами. Я распознаю некоторые ошибки или недоразумения LINQ для начинающих в вашем коде и надеюсь устранить их ниже.

  1. Если вы ожидаете существование одного элемента, не используйте Descendants. Вместо этого используйте Element. Все значения от «Pool Num» до «WA CAP» - это отдельный элемент XML, который можно получить непосредственно следующим образом: parent.Element(p + "PoolNumber"), где parent - родительский элемент нужного элемента.
  2. Чтобы получить значение элемента, используйте свойство Value: parent.Element(p + "PoolNumber").Value. Использование (string) приведение не является неправильным, однако предпочтительно использовать его, когда вы подозреваете, что элемент может или не может существовать. Если его не существует, вызов Value вернет NullReferenceException, поскольку он будет нулевым. Кастинг это обходит. Простой способ проверить это в моем коде ниже - добавить pool.Element(p + "PoolNumber").Remove(); после объявления pool и посмотреть, как он сломается. Затем используйте свой подход (string) и наблюдайте, как он счастливо продолжается.
  3. Относительно пункта # 1, подход Element эффективно заменяет необходимость foreach на результат, чтобы получить только одно значение. Я рекомендую поиграть с First, Single, FirstOrDefault и SingleOrDefault методами. У вас есть LINQPad, так что ознакомьтесь с примерами и поиграйте с ними.

Кроме того, имена локальных переменных должны начинаться со строчной буквы в соответствии со стандартными ожиданиями форматирования. Также полезно расположить вызовы методов LINQ на отдельных строках и выровнять их в начале точечной нотации.

Как мне получить запрашиваемые элементы в появляются горизонтальные и с некоторыми Разделитель

Используйте метод String.Join. В .NET 4.0 нет необходимости вызывать ToArray, так как метод принимает перегрузку для IEnumerable<string>.

В настоящее время мой код работает только для 1 XML файл. Как это можно изменить в цикле для нескольких файлов?

Поместите значения вашего пула в список, затем передайте его поверх него и поместите логику в тело цикла. Смотрите мой код ниже.

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

// load from websites based on pool numbers in list
var list = new List<string> { "510299", "510300"};
foreach (var poolNumber in list)
{
    XElement tags=XElement.Load("http://fapt.efanniemae.com/epooltalk-hvd/pool.xml?type=XML&pn=" + poolNumber + ".XML");
    XNamespace p = tags.GetDefaultNamespace();

    // export process

    XElement pool = tags.Element(p + "Pool");
    Console.WriteLine("Pool Num |" + pool.Element(p + "PoolNumber").Value);
    Console.WriteLine("CUSIP |" + pool.Element(p + "CUSIP").Value);
    Console.WriteLine("PoolPrefix |" + pool.Element(p + "PoolPrefix").Value);
    Console.WriteLine("Orig. Bal |" + pool.Element(p + "OriginalSecurityBalance").Value);
    Console.WriteLine("Orig. Term |" + pool.Element(p + "WeightedAverageOrigLoanTerm").Value);
    Console.WriteLine("Remain Term |" + pool.Element(p + "WAMnthsRemainingToAmortization").Value);
    Console.WriteLine("WALA |" + pool.Element(p + "WeightedAverageLoanAge").Value);
    Console.WriteLine("Net Rate |" + pool.Element(p + "CurrentAccrualRate").Value);
    Console.WriteLine("WA Margin |" + pool.Element(p + "WeightedAverageLoanMarginRate").Value);
    Console.WriteLine("SubType |" + pool.Element(p + "SubType").Value);
    Console.WriteLine("Updated CAP |" + pool.Element(p + "UpdatedCap").Value);
    Console.WriteLine("Issue Date |" + pool.Element(p + "IssueDate").Value);
    Console.WriteLine("Maturity Date |" + pool.Element(p + "MaturityDate").Value);
    Console.WriteLine("Rate Adj Freq |" + pool.Element(p + "RateAdjustmentFrequency").Value);
    Console.WriteLine("Period Cap |" + pool.Element(p + "PerAdjustmentCap").Value);
    Console.WriteLine("Pymt Chg Freq |" + pool.Element(p + "PaymentChangeFrequency").Value);
    Console.WriteLine("WA MTR |" + pool.Element(p + "WeightedAverageMonthsToRoll").Value);
    Console.WriteLine("WA CAP |" + pool.Element(p + "WeightedAverageCap").Value);

    var poolFactors = pool.Element(p + "PoolFactors");
    var months = poolFactors.Descendants(p + "Month")
                            .Select(m => m.Value);
    Console.WriteLine("Months |" + String.Join(", ", months.ToArray()));

    var wacs = poolFactors.Descendants(p + "WAC")
                          .Select(wac => wac.Value);
    Console.WriteLine("WAC |" + String.Join(", ", wacs.ToArray()));

    var wams = poolFactors.Descendants(p + "WAM")
                          .Select(wam => wam.Value);
    Console.WriteLine("WAM |" + String.Join(", ", wams.ToArray()));

    var factors = poolFactors.Descendants(p + "Factor")
                             .Select(f => f.Value);
    Console.WriteLine("Factor |" + String.Join(", ", factors.ToArray()));

    Console.WriteLine();
}
...