Linq to XML 简单查询

发布于 2024-08-20 21:05:53 字数 495 浏览 2 评论 0原文

我没有得到这个 Linq 的东西。我可以编写复杂的 SQL 查询并编写了一些 xpath。我正在尝试学习 Linq to XML,尽管仔细研究了我可以用 Google 搜索到的每个简洁示例,但我还是无法通过第一次尝试。

给定 XML:

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

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

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

I am not getting this Linq thing. I can write complex SQL queries and have written a few xpaths. I am trying to learn Linq to XML and cannot get past my first try despite poring over every terse example I can Google.

Given XML:

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

Query input is "Acme", "RobotOne", "MoveBack", I want output "MVBK"

Not sure if this is the best way to construct the XML how would I do it with elements instead of attributes? There are a few manufacturers and models and lots of codes

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

天暗了我发光 2024-08-27 21:05:53

如果您在整个 LINQ to XML 方面遇到困难,我建议稍微退后一步,只看一下 linq 的一般情况。我相信 linq 非常值得付出努力,并且会为您带来数倍的回报——特别是当我们迁移到众核世界时。

我建议您暂时忘记 XML 部分,而只考虑 linq 正在做什么——在您了解 linq 的用途之后,它将使 XML 的专门内容更容易理解。它帮助我在考虑 linq 时牢记以下两件事:

  1. 它在实现接口 IEnumerable的任何事物上表达标准“运算符”(扩展方法)。换句话说,linq只是一个方法调用。有些方法接受代码 (Func),有些方法 (First()) 则不接受。
  2. 这使您可以不必担心这些运算符如何为您提供结果,而更多地关注声明您的意图

对于我们这些命令式语言人员来说,很难放弃如何获得结果的详细(过度)规范。 linq 基本上为我们提供了一种声明我们想要结果的方法,而无需确切说明如何获得它。这就是为什么,在我看来,学习 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 相同的事情的(实际上它与现实中的非常接近)——但是你不知道没有编译器的所有绒毛。 linq 版本中的粘性更少,意图更多。另一种说法是,linq 正在隐式地为您执行无聊的样板文件,允许您的代码只显示有趣的部分。

从函数的角度来说,where() 方法是一个高阶函数。这仅仅意味着它是一个本身接受或返回一个函数的函数。 Where() 接受一个lambda——循环中有趣的部分。 lambda 是 C# 中的匿名函数,因此Where() 接受一个函数,因此它是高阶。我提到这一点是因为这个概念非常强大,是理解 linq 的关键,并让我们深入了解 linq 实际上是如何融入 C# 的全新编程模型(函数式编程)。获得这种“高阶性”对于理解 linq 非常有帮助。

使用直接 List我认为,诸如上面的查询是了解 linq 工作原理的最佳方式。当您发现自己对简单的 L-2-O 查询感到满意后,请再次查看 XML 内容——我想您会发现它们会更有意义。

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

If you are having difficulty with the whole LINQ to XML thing, I'd suggest taking a slight step back and just look at linq in general. I believe that linq is very much worth the effort and will repay you many times over--particularly as we migrate to the manycore world.

I'd suggest that you forget the XML part for a moment and just think about what linq is doing--after you get what linq is about, then it will make the specialized stuff for XML easier to grok. It helps me to think about linq with these two things in mind:

  1. It is expressing standard 'operators' (extension methods) on anything that implements the interface IEnumerable<T> In other words, linq is just a method call. Some of the methods take in code (Func), some (First()) do not.
  2. this allows you to be less concerned with the guts of how these operators give you the result, and more of just declaring your intent.

It's difficult for us imperative language folks to let go of the minute (over)specification of how to get a result. What linq is basically giving us is a way to declare we want a result without saying exactly how to get it. This is why, IMO, linq is so important to learn, for it is this exact 'decoupling' of intent from the exact 'how' that is the really cool part of linq. Basically it grafts in functional language concepts into poor old imperative C#.

A helpful learning aid for LINQ is to write the C# imperative code that would do the same thing. Do this a few times, and suddenly you will see the pattern that linq does for you. So, for example, consider a 'linq-to-object' declaration.

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

the analog in C# for the linq query would be:

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

I think it is fairly easy to see in this example how linq is doing the same thing as the foreach (it is actually very close to this in the reality)--but you don't have all the fluff that is just compiler goo. There is less goo and more intent in the linq version. Another way of putting this is that linq is doing the boring boilerplate stuff for you implicitly allowing your code to just show the interesting part.

In functional speak, the where() method is a higher order function. This just means that it is a function that itself takes or returns a function. Where() takes a lambda--the interesting part of the loop. The lambda is an anonymous function in C#--so Where() takes a function, thus it is higher order. I mention this because this concept is so powerful and key to understanding linq and grants insight into how linq is actually a whole new programmatic model (functional programming) bolted into C#. Getting this 'higher-order-ness' is very helpful to understanding linq.

Working with the straight List<T> queries such as the one above, I think, is the best way to wrap your head around how linq works. Afer you find yourself feeling comfortable with the straignt L-2-O queries, then look again at the XML stuff--I think you will find they will then make more sense.

With the Reactive framework due out in .NET 4.0, we will see linq expand to not only 'pull' queries but to 'push' scenarios too. I.e: the collection will fire code when it changes. It is my belief that the concepts of functional programming (of which linq is C#'s tunnel into) are the most likely bread and butter ways we will handle concurrency and parallel problems, so it is very important to learn them--not just to make our code more concise.

枕花眠 2024-08-27 21:05:53

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

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 组合。

Assuming that you're using this initialization:

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);

You can get the result via the following LINQ query:

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();

If this XML is nested further down, you'll need more Select/Where combinations.

追星践月 2024-08-27 21:05:53

不完全是答案,但是 XPath 不会让代码变得更简单吗?

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

Not quite the answer, but would not XPath make the code a little bit easier?

  var result1 = XDocument
            .Load("test.xml")
            .XPathSelectElements("/Manufacturer[@ManufacturerName='Acme']/Model[@ModelName='RobotOne']/CommandText[@CommandName='MoveBack']")
            .FirstOrDefault().Value;
太阳公公是暖光 2024-08-27 21:05:53

我已经扩展了您的 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,则需要使用根:xml.Root.Elements(...)

I've expanded your XML and demonstrate how to get the MoveBack command text. I've also added an example to grab the robot models for a particular manufacturer and list each robot's commands. The first example is broken down to demonstrate how to walk the XML structure to get an element at a time. The second example is done in one query. Of course this depends on how well you know your data. You should use SingleOrDefault and check for null before using a result if you expect it not to exist. Also, checking for attributes is important if they don't exist. This code assumes the XML is complete.

Regarding the structure of the XML it looks fine. Keeping the CommandText generic allows different commands to be supported. If the commands are always the same they could be their own elements. You could make the model name its own element, but leaving it as is - as an attribute - makes sense.

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();
}

If you decide to use an XDocument instead you'll need to use the Root: xml.Root.Elements(...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文