【问题标题】:c# Elements of elements using XElementc# Elements of elements using XElement
【发布时间】:2017-06-10 07:46:28
【问题描述】:

不知道我应该怎么做。

这是我正在使用的 XML:

<configuration>
    <tag1>
        <interestingstuff>avalue</interestingstuff>
    </tag1>

    <tag2>
         <item_type1 name="foo">
             <item_type2 name="bar">
                 <property>value1</property>
                 <property>value2</property>
             </item_type2>
             <item_type2 name="pub">
                 <property>valueX</property>
                 <property>valyeY</property>
             </item_type2>
          <item_type1>

          <item_type1 name="foo2">
              <item_type2 name="pub">
                  <property>valueX</property>
              </item_type2>
          </item_type1>
      </tag2>
</configuration>

我正在编写一个函数,它传递 item_type 和 item_type2 的值,并返回该组合的属性值列表。

这就是我所拥有的。它会在指出的地方引发“对象未设置为对象的实例”异常。

ArrayList properties = new ArrayList();
XDocument config = new XDocument();
config = XDocument.Parse(XML_Text);
foreach (XElement item1 in config.Root.Element("tag2").Nodes())
{
    if (item1.Attribute("name").Value.ToString() == passed_item1_value)
    {
      //this is where it's breaking
      foreach (XElement item2 in item1.Elements("item_type2").Nodes())
      {
        if item2.Attribute("name").Value.ToString() == passed_item2_value)
        {
           foreach (XElement property in item2.Elements("property").Nodes())
           {
             properties.Add(property.Value.ToString());
           }
           break;
         }
       }
       break;
     }
}

我知道这没有意义 - 但我无法让它有意义。

【问题讨论】:

  • 如何“破”?始终包含完整的错误消息。
  • 直接的答案是(我猜)XElement 不能迭代 Nodes()。但是下面的 Linq 答案要好得多。
  • Breaking = "对象引用未设置为对象的实例" 异常。这是预期的。我发现了 Henk Holterman 刚刚发布的同一件事,即我正在尝试迭代一个不可迭代的对象。但是当我试图找到正确的方法时,我却让自己感到困惑。
  • 删除 Nodes() foreach (XElement item2 in item1.Elements("item_type2"))
  • 你看,我猜错了。但是请务必将其添加到问题中。此处不计评论。

标签: c# .net xml linq xelement


【解决方案1】:

我会这样做:

public IEnumerable<string> findValues(string item1, string item2)
{
    config = XDocument.Parse(XML_Text)
    var res = config.Descendants("item_type1")
                    .Where(x=>x.Attribute("name").Value == item1)
                    .Descendants("item_type2")
                    .Where(x=>x.Attribute("name").Value == item2);
    return res.Descendants("property").Select(x=>x.Value);
}

【讨论】:

  • 看起来更优雅!不过忘了添加,数据可能不包含匹配的节点(item_type1 或 item_type2)。这会破坏这个解决方案吗?
  • @GregorySloan 你会得到空集合回来,但也不例外
  • 只是一个旁注,但 Descendants() 忽略所有文档结构。它还将在许多级别下找到&lt;item_type1&gt;。这并不总是一个功能。
  • @HenkHolterman 说得好,我知道,但是对于这个无关紧要的示例xml结构,如果真正的xml不同,他必须考虑使用具有完整路径的元素
  • 完美运行。在这种情况下,肯定不会有不同级别的重复标签,并且每个子元素中的值都是唯一的,所以这很好。
【解决方案2】:

我猜你想要XPath 查询,如下所示:

var path = string.Join("/", 
    "configuration",
    "tag2",
    string.Format("item_type1[@name='{0}']", passed_item1_value),
    string.Format("item_type2[@name='{0}']", passed_item2_value),
    "property");

var elements = (IEnumerable)config.XPathEvaluate(path);

var properties = elements.Cast<XElement>().Select(x => x.Value);

不要忘记包含using System.Xml.XPath;,这是XPathEvaluate 定义的。

【讨论】:

    【解决方案3】:
    using System.Linq;
    using System.Xml.Linq;
    
    IEnumerable<string> GetProperties(XElement xml, string item1, string item2)
    {
        return xml.Element("tag2")
            .Elements("item_type1").Where(x => x.Attribute("name").Value == item1)
            .Elements("item_type2").Where(x => x.Attribute("name").Value == item2)
            .SelectMany(x => x.Elements("property").Select(p => p.Value));
    }
    

    与上述许多解决方案类似,但使用数据的直接路径并使用SelectMany 将结果转换为单个集合 - 即,如果您有 item1 和 item2 的重复项,则可以使用。

    【讨论】:

      【解决方案4】:

      如果 VB'er 需要类似的帮助

          Dim xe As XElement
          xe = <configuration>
                   <tag1>
                       <interestingstuff>avalue</interestingstuff>
                   </tag1>
      
                   <tag2>
                       <item_type1 name="foo">
                           <item_type2 name="bar">
                               <property>value1</property>
                               <property>value2</property>
                           </item_type2>
                           <item_type2 name="pub">
                               <property>valueX</property>
                               <property>valyeY</property>
                           </item_type2>
                       </item_type1>
      
                       <item_type1 name="foo2">
                           <item_type2 name="pub">
                               <property>valueX</property>
                           </item_type2>
                       </item_type1>
                   </tag2>
               </configuration>
      
          Dim passed_item1_value As String = "foo" 'for test
          Dim passed_item2_value As String = "pub" 'for test
      
          Dim ie As IEnumerable(Of String)
          ie = (From el In xe.<tag2>.<item_type1>...<item_type2>
                Where el.Parent.@name = passed_item1_value AndAlso el.@name = passed_item2_value
                From sel In el...<property> Select sel.Value)
      
          Return ie.ToArray
      

      【讨论】:

        猜你喜欢
        • 2022-12-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-01
        • 2022-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多