通过 List 从 XML 获取 InnerText

Get InnerText from XML via List

我从服务收到以下字符串列表

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<entry>
<id>1</id>
<title>Job 1</title>
<author>
<name>Jim James</name>
</author>
<modified>2018-08-10T14:50:46-04:00</modified>
</entry>
<entry>
<id>2</id>
<title>Job 2</title>
<author>
<name>John Smith</name>
</author>
<modified>2018-09-10T14:50:46-04:00</modified>
</entry>
<entry>
<id>3</id>
<title>Job 3</title>
<author>
<name>Paul Rain</name>
</author>
<modified>2018-06-10T14:50:46-04:00</modified>
</entry>
<entry>
<id>4</id>
<title>Job 4</title>
<author>
<name>Jim James</name>
</author>
<modified>2018-08-10T14:50:46-04:00</modified>
</entry>
</feed>

我试图通过内部文本获取 name 的值,但无法这样做:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response); //assuming response is above xml string
XmlNodeList parentNode = xmlDoc.GetElementsByTagName("entry");
foreach (XmlNode childrenNode2 in parentNode)
{
    var b = childrenNode2.SelectSingleNode("name").InnerText ?? string.Empty;
}

相反,我得到的是 object is null 异常。

 "ExceptionMessage": "Object reference not set to an instance of an object.",

感谢任何指点。

给定的样本无效XML:

  • 它没有文档元素
  • 它没有 XML 声明
  • 它包含未闭合的打开元素<author>

由于这些原因中的任何一个,我不希望它被解析。

然而,NullReferenceException 没有正确传达失败的原因,因此这里可能还有其他原因。使用您的调试器确定哪一行抛出此异常。

假设您遗漏了一些 XML 而它实际上是有效的,这也可能是由于未指定您打算 select 的元素的名称空间造成的。如果文档具有目标命名空间 http://some-namespaceentry 不是 select 的正确名称;您必须包括名称空间。 GetElementsByTagName 有一个 overload with two arguments,其中第二个是命名空间。

你这里有几个问题:

  1. 您使用的是旧的 XmlDocument API,它对命名空间的处理有些不便。具体来说:

    • XmlNode.GetElementsByTagName(string) 命名空间未知的 。它匹配 "raw" 限定元素名称,可能包括前缀。

      这个方法,微软写在documentation

      Note

      It is recommended that you use the XmlNode.SelectNodes or XmlNode.SelectSingleNode method instead of the GetElementsByTagName method.

    • XmlNode.SelectSingleNode(string),相反,是命名空间感知的,而select只是空命名空间中的元素。

      来自docs

      If the XPath expression does not include a prefix, it is assumed that the namespace URI is the empty namespace. If your XML includes a default namespace, you must still use the XmlNamespaceManager and add a prefix and namespace URI to it; otherwise, you will not get a selected node.

    这两种方法之间的这种不一致解释了为什么您的代码部分有效,因为您的 XML 文档中的所有元素都属于 http://purl.org/atom/ns# 命名空间。

  2. 如果缩进 XML,显然 <name> 节点嵌套在容器中 <author> 节点:

    <feed version="0.3" xmlns="http://purl.org/atom/ns#">
      <entry>
        <id>1</id>
        <title>Job 1</title>
        <author>
          <name>Jim James</name>
        </author>
        <modified>2018-08-10T14:50:46-04:00</modified>
      </entry>
      <!--Other entries omitted -->
    </feed>
    

    由于存在此中间元素,您对 SelectSingleNode("name") 的调用也失败了。

因此下面的代码将工作并正确select您的条目名称:

var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response); //assuming response is above xml string

var manager = new XmlNamespaceManager(xmlDoc.NameTable);
manager.AddNamespace("atom", @"http://purl.org/atom/ns#");

var parentNode = xmlDoc.DocumentElement.SelectNodes("./atom:entry", manager);
foreach (XmlNode childrenNode2 in parentNode)
{
    var name = childrenNode2.SelectSingleNode("./atom:author/atom:name", manager)?.InnerText ?? "";
    Console.WriteLine(name);
}           

示例 fiddle #1 here.

顺便说一句,这一切都可以用 LINQ to XML API 更方便地完成,完全避免需要使用 XmlNamespaceManager 和 XPath 等等:

var xmlDoc = XDocument.Parse(response);

var ns = (XNamespace)@"http://purl.org/atom/ns#";
foreach (var element in xmlDoc.Root.Elements(ns + "entry"))
{
    var name = element.Element(ns + "author")?.Element(ns + "name")?.Value ?? "";
    Console.WriteLine(name);
}           

示例 fiddle #2 here.