Linq to XML Простой запрос - PullRequest
       7

Linq to XML Простой запрос

6 голосов
/ 05 февраля 2010

Я не получаю эту вещь Linq. Я могу написать сложные запросы SQL и написал несколько путей xpath. Я пытаюсь выучить Linq для XML и не могу справиться со своей первой попыткой, несмотря на то, что перебираю каждый краткий пример, который могу найти в Google.

С учетом XML:

<Manufacturer ManufacturerName="Acme">
 <Model ModelName="RobotOne">
  <CommandText CommandName="MoveForward">MVFW</CommandText>
  <CommandText CommandName="MoveBack">MVBK</CommandText>

Ввод запроса: "Acme", "RobotOne", "MoveBack", я хочу вывод "MVBK"

Не уверен, что это лучший способ построить XML, как бы я делал это с элементами вместо атрибутов? Есть несколько производителей и моделей и множество кодов

Ответы [ 4 ]

5 голосов
/ 05 февраля 2010

Если у вас возникли сложности со всем, что связано с LINQ to XML, я бы посоветовал сделать небольшой шаг назад и просто взглянуть на linq в целом. Я считаю, что linq очень стоит усилий и окупит вас много раз, особенно когда мы переместимся в многоядерный мир.

Я бы посоветовал вам на мгновение забыть часть XML и просто подумать о том, что делает linq - после того, как вы поймете, что такое linq, тогда это упростит поиск специализированных материалов для XML. Это помогает мне думать о linq с учетом этих двух вещей:

  1. Он выражает стандартные «операторы» (методы расширения) для всего, что реализует интерфейс IEnumerable Другими словами, linq - это просто вызов метода. Некоторые методы принимают код (Func), некоторые (First ()) - нет.
  2. это позволяет вам меньше беспокоиться о том, как эти операторы дают вам результат, и больше просто о том, что объявляет ваше намерение .

Нам, насущным языковым специалистам, трудно отказаться от мелкой (сверх) спецификации как получить результат. Что нам в основном дает linq, так это способ объявить , что мы хотим получить результат, не говоря точно, как его получить. Вот почему, IMO, linq так важно выучить, поскольку именно это «разделение» намерений от точного «как» является действительно классной частью linq. В основном это прививает в функциональных концепциях языка старый плохой императив C #.

Полезным средством обучения для LINQ является написание императивного кода на C #, который будет делать то же самое . Сделайте это несколько раз, и вдруг вы увидите шаблон , который linq делает для вас. Так, например, рассмотрим объявление 'linq-to-object'.

string[] names= new string[] { "kevin", "tristan", jen" };
var result = names.where(n => n.length() > 3);

аналог в C # для запроса linq будет:

List<string> list = new List<string>();
foreach(string n in names)
{
    if (n.length > 3) list.add(n);
}

Я думаю, что в этом примере довольно легко увидеть, как linq делает то же самое, что и foreach (на самом деле это очень близко к этому), но у вас нет всего пуха, который просто компилятор Goo. В версии linq меньше уловок и больше намерений. Другой способ выразить это в том, что linq неявно делает для вас скучный шаблонный материал , позволяя вашему коду просто показать интересную часть.

В функциональном смысле, метод where () является функцией более высокого порядка . Это просто означает, что это функция, которая сама принимает или возвращает функцию. Где () принимает лямбда - интересная часть цикла . Лямбда - это анонимная функция в C #, поэтому Where () принимает функцию, поэтому она высшего порядка . Я упоминаю об этом, потому что эта концепция является настолько мощной и ключевой для понимания linq и дает представление о том, как на самом деле linq представляет собой совершенно новую программную модель (функциональное программирование), встроенную в C #. Получение этого «высшего порядка» очень полезно для понимания linq.

Работа с прямыми запросами List , такими как приведенный выше, я думаю, является наилучшим способом понять, как работает linq. Если вы чувствуете себя комфортно с прямыми запросами L-2-O, а затем снова посмотрите на XML - я думаю, вы обнаружите, что тогда они будут иметь больше смысла.

С появлением платформы Reactive в .NET 4.0 мы увидим, что linq расширяется не только для «вытягивания» запросов, но и для «проталкивания» сценариев. Т.е .: коллекция сгенерирует код при изменении. Я полагаю, что концепции функционального программирования (с которым linq является туннелем C #) являются наиболее вероятным способом решения проблем параллелизма и параллелизма, поэтому очень важно изучить их, а не просто код более краткий.

4 голосов
/ 05 февраля 2010

Предполагается, что вы используете эту инициализацию:

string xml = @"
<Manufacturer ManufacturerName='Acme'>
 <Model ModelName='RobotOne'>
  <CommandText CommandName='MoveForward'>MVFW</CommandText>
  <CommandText CommandName='MoveBack'>MVBK</CommandText>
</Model>
</Manufacturer>";

XElement topElement = XElement.Parse(xml);

Результат можно получить с помощью следующего запроса LINQ:

string commandText = topElement.Elements("Model")
    .Where(element => (string)element.Attribute("ModelName") == "RobotOne")
    .Elements("CommandText")
    .Where(element => (string)element.Attribute("CommandName") == "MoveBack")
    .Select(element => element.Value)
    .FirstOrDefault();

Если этот XML вложен дальше, вам понадобится больше комбинаций выбора / где.

2 голосов
/ 05 февраля 2010

Не совсем ответ, но разве XPath не сделает код немного проще?

  var result1 = XDocument
            .Load("test.xml")
            .XPathSelectElements("/Manufacturer[@ManufacturerName='Acme']/Model[@ModelName='RobotOne']/CommandText[@CommandName='MoveBack']")
            .FirstOrDefault().Value;
1 голос
/ 05 февраля 2010

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

Что касается структуры XML, то все выглядит хорошо. Сохранение универсального CommandText позволяет поддерживать различные команды. Если команды всегда одинаковы, они могут быть их собственными элементами. Вы можете сделать имя модели своим собственным элементом, но оставить его как есть - как атрибут - имеет смысл.

string input = @"<root>
    <Manufacturer ManufacturerName=""Acme"">
        <Model ModelName=""RobotOne"">
            <CommandText CommandName=""MoveForward"">MVFW</CommandText>
            <CommandText CommandName=""MoveBack"">MVBK</CommandText>
        </Model>
        <Model ModelName=""RobotTwo"">
            <CommandText CommandName=""MoveRight"">MVRT</CommandText>
            <CommandText CommandName=""MoveLeft"">MVLT</CommandText>
        </Model>
    </Manufacturer>
    <Manufacturer ManufacturerName=""FooBar Inc."">
        <Model ModelName=""Johnny5"">
            <CommandText CommandName=""FireLaser"">FL</CommandText>
            <CommandText CommandName=""FlipTVChannels"">FTVC</CommandText>
        </Model>
        <Model ModelName=""Optimus"">
            <CommandText CommandName=""FirePlasmaCannon"">FPC</CommandText>
            <CommandText CommandName=""TransformAndRollout"">TAL</CommandText>
        </Model>
    </Manufacturer>
</root>";
var xml = XElement.Parse(input);

// get the Manufacturer elements, then filter on the one named "Acme".
XElement acme = xml.Elements("Manufacturer")
                   .Where(element => element.Attribute("ManufacturerName").Value == "Acme")
                   .Single(); // assuming there's only one Acme occurrence.

// get Model elements, filter on RobotOne name, get CommandText elements, filter on MoveBack, select single element
var command = acme.Elements("Model")
                  .Where(element => element.Attribute("ModelName").Value == "RobotOne")
                  .Elements("CommandText")
                  .Where(c => c.Attribute("CommandName").Value == "MoveBack")
                  .Single();

// command text value
string result = command.Value;
Console.WriteLine("MoveBack command: " + result);

// one unbroken query to list each FooBar Inc. robot and their commands
var query = xml.Elements("Manufacturer")
               .Where(element => element.Attribute("ManufacturerName").Value == "FooBar Inc.")
               .Elements("Model")
               .Select(model => new {
                    Name = model.Attribute("ModelName").Value,
                    Commands = model.Elements("CommandText")
                                    .Select(c => new {
                                        CommandName = c.Attribute("CommandName").Value,
                                        CommandText = c.Value
                                    })
               });

foreach (var robot in query)
{
    Console.WriteLine("{0} commands:", robot.Name);
    foreach (var c in robot.Commands)
    {
        Console.WriteLine("{0}: {1}", c.CommandName, c.CommandText);
    }
    Console.WriteLine();
}

Если вы решили использовать XDocument, вам нужно будет использовать Root: xml.Root.Elements(...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...