【问题标题】:Basic LINQ syntax基本 LINQ 语法
【发布时间】:2009-11-06 20:02:57
【问题描述】:

假设你有一个像这样的 XML:

 <data>
  <messages>
   <message name="Person" id="P">
    <field name="FirstName" required="Y" />
    <field name="LastName" required="Y" />
    <field name="Sex" required="N" />
   </message>
   <message name="Car" id="C">
    <field name="Make" required="Y" />
    <field name="Model" required="Y" />
    <field name="Year" required="N" />
   </message>
  </messages>
 </data>

使用 Linq,您如何获得 Person 的所有必需字段名称的列表?

我今天刚开始使用 LINQ/XML,这大概是我所了解的。

    var q = from c in loaded.Descendants("field")
            where (string)c.Attribute("required") == "Y" &&
            // how to check the parent node (message) has an attribute (id="P")           
            select (string)c.Attribute("name");

    foreach (string name in q)
        Console.WriteLine(name);

【问题讨论】:

  • 您没有这样的 XML,它不是有效的 XML 文档。您可以发布您拥有的实际 XML,或者至少是一个工作示例吗?

标签: c# xml linq parent-node


【解决方案1】:

我为消息添加了一个根元素和结束标记以使 XML 有效:

XDocument loaded = XDocument.Parse(@"
  <messages>
    <message name=""Person"" id=""P"">
      <field name=""FirstName"" required=""Y"" />
      <field name=""LastName"" required=""Y"" />
      <field name=""Sex"" required=""N"" />
    </message>
    <message name=""Car"" id=""C"">
      <field name=""Make"" required=""Y"" />
      <field name=""Model"" required=""Y"" />
      <field name=""Year"" required=""N"" />
    </message>
  </messages>");

不要查找所有字段,然后检查每个字段的父级,而是查找您感兴趣的一个父级,以便您检查的字段更少:

IEnumerable<string> fields =
  loaded.Root.Elements()
  .Where(m => m.Attribute("id").Value == "P")
  .Single()
  .Elements("field")
  .Where(f => f.Attribute("required").Value == "Y")
  .Select(f => f.Attribute("name").Value);

编辑:
为子元素添加说明符“字段”,以防消息元素包含任何其他类型的元素。

编辑 2:
我将一个工作示例与实际数据的子集放在一起:

XDocument loaded = XDocument.Parse(@"
  <fix major=""4"" minor=""4"">
    <header>
    </header>
    <trailer>
    </trailer>
    <messages>
      <message name=""ResendRequest"" msgtype=""2"" msgcat=""admin"">
        <field name=""BeginSeqNo"" required=""Y"" />
        <field name=""EndSeqNo"" required=""Y"" />
      </message>
      <message name=""Reject"" msgtype=""3"" msgcat=""admin"">
        <field name=""RefSeqNum"" required=""Y"" />
        <field name=""RefTagID"" required=""N"" />
        <field name=""RefMsgType"" required=""N"" />
        <field name=""SessionRejectReason"" required=""N"" />
        <field name=""Text"" required=""N"" />
        <field name=""EncodedTextLen"" required=""N"" />
        <field name=""EncodedText"" required=""N"" />
      </message>
    </messages>
  </fix>");

IEnumerable<string> fields =
  loaded.Root.Element("messages").Elements("message")
  .Where(m => m.Attribute("name").Value == "Reject")
  .Single()
  .Elements("field")
  .Where(f => f.Attribute("required").Value == "Y")
  .Select(f=>f.Attribute("name").Value);

【讨论】:

  • 第二个人弄错了。 OQ说://如何检查父节点(消息)是否有属性(id="P") His Person has a Car.它不是人和车,它的人有车
  • @jfar:你到底在说什么?为什么会认为消息节点内部有消息节点?
  • 对,一个消息中有一个消息,而不是两个单独的消息。
  • 请不要对 XML 进行过多解读……我只是为了举例。
  • @jfar:这不能回答我的问题。为什么会认为消息节点内部有消息节点?
【解决方案2】:

您也可以通过执行以下操作来删除单独的 foreach 循环和丑陋的演员表

(from c in myXML.Descendants("field")
         where c.Attribute("required").Value == "Y" && 
         c.Parent.Attribute("id").Value == "P" 
         select c.Attribute("name").Value).ToList().ForEach(s => Console.WriteLine(s.ToString()));

编辑:

由于出现空属性或可选属性的问题,这里是一个如何添加扩展方法来处理空属性的示例,如果不存在,您可以传递一个默认值(即第二个参数)扩展方法)。

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

namespace MyTestApp
{

class Program
{
    static void Main(string[] args)
    {
        XDocument myXML = XDocument.Load(@"C:\file.xml"); 

        (from c in myXML.Descendants("field")
         where c.Attribute("required")
               .GetAttributeValueOrDefault("N") == "Y" && 
                c.Parent.Attribute("id").Value == "P"      
         select 
         c.Attribute("name").Value).ToList().ForEach(s => Console.WriteLine(s.ToString()));

        Console.ReadLine();
    }
}

public static class XLinqHelper
{
    // extension method that handles an xattribute and returns the provided default if the Xattrib is null
    public static string GetAttributeValueOrDefault(this XAttribute s, string defaultValue)
    {
        string retVal;
        if (s == null)
            retVal = defaultValue;
        else
            retVal = s.Value;
        return retVal;


    }
}

}

【讨论】:

  • 奇怪,当我使用 .Value 时出现 NullReferenceException,这适用于您和 Guffas 的示例。
  • 顶部的 xml 无效,因为消息元素没有结束标记。无论如何解析器都会拒绝,所以我假设你有有效的 XML
  • XDocument 加载 = XDocument.Load("file.xml");
  • 是的,xml 是有效的。我知道我有错别字并遗漏了一些东西。这只是一个例子。
  • string 是可空的(引用类型),所以当你转换它时,它只是转换为空字符串而没有问题,在属性不存在的地方,值不可用,因为你无法获得 null 的 value 属性。 . 如果这有意义
【解决方案3】:

考虑到提问者更改了他的xml,这个答案是完全错误的。我应该删除这个吗?

var q = from c in loaded.Descendants("field")
            where (string)c.Attribute("required") == "Y" &&
                    c.Parent.Attribute("id").Value == "P"
            select (string)c.Attribute("name");

添加我使用的 Xml,因为对正确的解决方案存在一些混淆。

XDocument loaded = XDocument.Parse(@"
<message name=""Person"" id=""P"">
    <field name=""FirstName"" required=""Y"" /> 
    <field name=""LastName"" required=""Y"" />
    <field name=""Sex"" required=""N"" />
    <message name=""Car"" id=""C"">
        <field name=""Make"" required=""Y"" />
        <field name=""Model"" required=""Y"" />
        <field name=""Year"" required=""N"" />
    </message>
</message>");

【讨论】:

  • 伙计,我和 c.Parent 很亲近,但只是没有看到属性...谢谢!
【解决方案4】:

您使用 System.Xml.Linq.XNodeSystem.Xml.Linq.XObject 定义的 Ancestors() 方法或 Parent 属性

【讨论】:

    猜你喜欢
    • 2015-03-30
    • 2012-03-28
    • 1970-01-01
    • 2012-02-08
    • 2013-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    相关资源
    最近更新 更多