Как вы разделяете XmlNodeList на два вложенных XmlNodeList? - PullRequest
0 голосов
/ 09 мая 2018

Как я могу разделить XmlNodeList на два меньших XmlNodeList, где один имеет размер N, а другой имеет общий размер - N?

См. Пример ниже и где я пытался пойти с этим:

    public static void Main(string[] args)
    {
        XmlDocument someDoc = new XmlDocument();
        someDoc.LoadXml(@"<bananas>
                            <banana tasty='yes'></banana>
                            <banana tasty='very'></banana>
                            <banana tasty='amazing'></banana>
                            <banana tasty='mind-blowing'></banana>
                            <banana tasty='disgusting'></banana>
                          </bananas>");
        XmlNodeList bananaNodeList = someDoc.SelectNodes("//banana");
        eatSomeBananas(bananaNodeList, 2);
    }

    /** Splits a XmlNodeList into two XmlNodeList, first one is a slice from 0 to numberOfBananas-1, and the other slice is from numberOfBananas and onwards
    */
    public static void eatSomeBananas(XmlNodeList subBananaNodeList, int numberOfBananas)
    {
        XmlNodeList bananasToEat = subBananaNodeList.Cast<XmlNode>().Take(numberOfBananas) as XmlNodeList; //Error down-casting - null!
        if (bananasToEat == null)
            Console.WriteLine("Error! Did not work");
        /*else 
            doSomethingHere(bananasToEat); */
        XmlNodeList remainingBananas = subBananaNodeList.Cast<XmlNode>().Skip(numberOfBananas) as XmlNodeList; //Error down-casting - null!
        eatSomeBananas(remainingBananas, numberOfBananas);
    }

Я пытался привести XmlNodeList к IEnumerable<XmlNode> (поскольку первое наследует от второго) - я считаю, что это должно быть откровением. Разве я не смогу потом вернуть это обратно в XmlNodeList? Но если нет, то почему?

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Разве я не смогу потом перевести это обратно в XmlNodeList? Но если нет, то почему?

Нет, поскольку значение, возвращаемое из Skip, не является XmlNodeList. Он объявлен только как IEnumerable<XmlNode>, и я ожидаю, что реализация Skip, вероятно, будет использовать блок итератора ... конечно, я был бы удивлен, если бы Skip имел какие-либо подробные знания о XmlNodeList. Take будет работать точно так же.

Лично я бы вообще не использовал старый API XML и просто использовал LINQ to XML. Это естественно работает с LINQ - и, как правило, лучше XML API, IMO.

У вас нет для этого, имейте в виду - вы могли бы просто изменить весь свой код, чтобы использовать IEnumerable<XmlNode> вместо XmlNodeList:

public static void eatSomeBananas(IEnumerable<XmlNode> subBananaNodeList, int numberOfBananas)
{
    IEnumerable<XmlNode> bananasToEat = subBananaNodeList.Take(numberOfBananas);
    IEnumerable<XmlNode> remainingBananas = subBananaNodeList.Skip(numberOfBananas);
    // Added condition to avoid infinite recursion
    if (remainingBananas.Any())
    {
        eatSomeBananas(remainingBananas, numberOfBananas);
    }
}

Тогда вы можете просто вызвать Cast один раз, когда вы вызываете метод:

eatSomeBananas(bananaNodeList.Cast<XmlNode>(), 2);

Вот версия LINQ to XML, которую я предпочитаю:

using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

public class Test
{
    public static void Main(string[] args)
    {
        XDocument someDoc = XDocument.Parse(
            @"<bananas>
                <banana tasty='yes'></banana>
                <banana tasty='very'></banana>
                <banana tasty='amazing'></banana>
                <banana tasty='mind-blowing'></banana>
                <banana tasty='disgusting'></banana>
              </bananas>");
        IEnumerable<XElement> bananas = someDoc.Descendants("banana");
        EatSomeBananas(bananas, 2);
    }

    public static void EatSomeBananas(IEnumerable<XElement> bananas, int numberOfBananas)
    {
        var bananasToEat = bananas.Take(numberOfBananas);
        Console.WriteLine("Eating some bananas"); 
        foreach (var element in bananasToEat)
        {
            var tasty = element.Attribute("tasty").Value;
            Console.WriteLine($"Tasty: {tasty}");
        }
        Console.WriteLine("Eaten the bananas");
        var remainingBananas = bananas.Skip(numberOfBananas);
        if (remainingBananas.Any())
        {
            EatSomeBananas(remainingBananas, numberOfBananas);
        }
    }
}

Обратите внимание, что для производственной реализации я бы избегал рекурсии и мог бы периодически материализовать результат - в противном случае он будет каждый раз повторяться с самого начала, пропуская загрузку, а затем принимая некоторые.

0 голосов
/ 09 мая 2018

Я только что преобразовал в IQueryable и использовал его, с которым гораздо проще работать, чем XmlNodeList.

    public static void Main(string[] args)
    {
        XmlDocument someDoc = new XmlDocument();
        someDoc.LoadXml(@"<bananas>
                        <banana tasty='yes'></banana>
                        <banana tasty='very'></banana>
                        <banana tasty='amazing'></banana>
                        <banana tasty='mind-blowing'></banana>
                        <banana tasty='disgusting'></banana>
                      </bananas>");
        XmlNodeList bananaNodeList = someDoc.SelectNodes("//banana");

        var allBananas = bananaNodeList.Cast<XmlNode>().AsQueryable();

        eatSomeBananas(allBananas, 2);
    }

    public static void eatSomeBananas(IQueryable<XmlNode> subBananas, int numberOfBananas)
    {
        var bananasToEat = subBananas.Take(numberOfBananas);
        var remainingBananas = subBananas.Skip(numberOfBananas);

        Console.WriteLine(string.Format("Bananas to eat: {0}", bananasToEat.Count()));
        Console.WriteLine(string.Format("Remaining bananas: {0}", remainingBananas.Count()));
        if (!bananasToEat.Any())
            Console.WriteLine("Error! Did not work (not enough bananas!)");
        else 
            eatSomeBananas(remainingBananas, numberOfBananas);
    }

Выход:

Bananas to eat: 2
Remaining bananas: 3
Bananas to eat: 2
Remaining bananas: 1
Bananas to eat: 1
Remaining bananas: 0
Bananas to eat: 0
Remaining bananas: 0
Error! Did not work (not enough bananas!)
...