Linq to XML Simple Query

时间:2010-02-05 04:40:39

标签: .net linq linq-to-xml

我没有得到这个Linq的东西。我可以编写复杂的SQL查询并编写了一些xpath。我正在努力学习Linq to XML并且无法通过我的第一次尝试,尽管仔细研究了我可以谷歌的每一个简洁例子。

鉴于XML:

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

查询输入是“Acme”,“RobotOne”,“MoveBack”,我想输出“MVBK”

不确定这是否是构建XML的最佳方式我如何使用元素而不是属性来实现?有一些制造商和型号以及许多代码

4 个答案:

答案 0 :(得分:5)

如果你遇到整个LINQ to XML的问题,我建议稍稍退一步看看linq。我相信 linq非常值得付出努力并会多次回报你 - 特别是当我们迁移到众多世界时。

我建议你暂时忘记XML部分,只考虑linq正在做什么 - 在你得到linq之后,然后它将使XML的专用内容更容易理解。这有助于我在考虑这两件事时考虑linq:

  1. 它在实现接口IEnumerable&lt; T&gt;的任何东西上表达标准的“运算符”(扩展方法)。换句话说,linq只是一个方法调用。一些方法接受代码(Func),一些(First())不接受。
  2. 这使您可以更少关注这些运算符如何为您提供结果的内容,而不仅仅是声明您的意图

我们的命令式语言人员很难放弃 how 的分钟(过度)规范来获得结果。 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);
用于linq查询的C#中的模拟将是:

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

我认为在这个例子中很容易看出linq是如何做与foreach相同的事情(实际上它实际上非常接近这一点) - 但你没有所有的绒毛只是编译器粘性。在linq版本中有更少的粘性和更多的意图。另一种说法是 linq隐含地为你做了无聊的样板文件允许你的代码只显示有趣的部分。

在功能说话中, where()方法是一个更高阶函数。这只意味着它是一个自身接受或返回一个函数的函数。 Where()采用 lambda - 循环的有趣部分。 lambda是C#中的匿名函数 - 所以Where()接受一个函数,因此它是高阶。我之所以提到这一点,是因为这个概念非常强大,也是理解linq的关键,并且可以深入了解linq实际上是一个全新的编程模型(函数式编程)如何用于C#。获得这个“更高阶的秩序”对于理解linq非常有帮助。

使用直接列表&lt; T&gt;我认为,如上所述的查询是了解linq如何工作的最佳方式。你发现自己对L-2-O查询感到满意,然后再看看XML的东西 - 我想你会发现它们会更有意义。

随着.NET 4.0中的Reactive框架的推出,我们将看到linq不仅扩展到“拉”查询,还扩展到“推送”方案。即:集合将在更改时触发代码。我相信函数式编程的概念(其中linq是C#的隧道)是我们处理并发和并行问题的最有可能的方法,因此学习它们非常重要 - 不仅仅是为了使我们的代码更简洁。

答案 1 :(得分:4)

假设您正在使用此初始化:

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进一步嵌套,则需要更多Select / Where组合。

答案 2 :(得分:2)

不完全是答案,但XPath不会使代码更容易一些吗?

  var result1 = XDocument
            .Load("test.xml")
            .XPathSelectElements("/Manufacturer[@ManufacturerName='Acme']/Model[@ModelName='RobotOne']/CommandText[@CommandName='MoveBack']")
            .FirstOrDefault().Value;

答案 3 :(得分:1)

我扩展了您的XML并演示了如何获取MoveBack命令文本。我还添加了一个示例来获取特定制造商的机器人模型并列出每个机器人的命令。第一个示例分解为演示如何遍历XML结构以一次获取一个元素。第二个示例在一个查询中完成。当然,这取决于您对数据的了解程度。如果您希望它不存在,您应该使用SingleOrDefault并在使用结果之前检查null。此外,如果属性不存在,则检查属性很重要。此代码假定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(...)