C# LINQ读取XML节点为空、无法获取带命名空间属性值问题
C# 读取XML两类常见问题修复
问题复现
存在如下XML结构:
<items> <item> <title>Weź udział w grze historycznej</title> <link>https://legionowo.pl/a/wez-udzial-w-grze-historycznej</link> <description><![CDATA[<img src="https://legionowo.pl/img/artykuly/4/2022_07/x41594.jpg.pagespeed.ic.9BJkaqvzdK.jpg%22%3E<br><br>]]></description> <pubDate>Fri, 01 Jul 2022 09:10:50 +0000</pubDate> <media:content url="https://legionowo.pl/img/artykuly/4/2022_07/x41594.jpg.pagespeed.ic.9BJkaqvzdK.jpg" medium="image" /> <guid isPermaLink="false">https://legionowo.pl/a/wez-udzial-w-grze-historycznej</guid> </item> <items>
编写如下C#方法读取XML:
private async Task _XElementGet() { XElement doc = XElement.Load("C:\\mail.xml"); List<XElement> list; list = (from prod in doc.Elements("item") select prod).ToList(); foreach(XElement item in list) { Console.WriteLine(item.Name); } }
执行该方法后获取到的item列表为空,但改用XDocument加载XML、调用Descendants("item")查询时可正常获取节点,对应代码如下:
private async Task _XDocumentGet() { XDocument doc = XDocument.Load("C:\\mail.xml"); List<XElement> list; list = (from prod in doc.Descendants("item") select prod).ToList(); foreach(XElement item in list) { //Console.WriteLine(item.Element("title")); Console.WriteLine(item.FirstAttribute.Value); } }
尝试读取media:content节点的属性值时,抛出如下异常:
System.Xml.XmlException: 'The ':' character, hexadecimal value 0x3A, cannot be included in a name.'
无法正常读取该节点的属性值。
原因与修复方案
问题1:XElement.Elements() 返回空列表
核心原因有两点:
Elements()方法只会查找当前节点的直接一级子节点,不会递归遍历多层后代节点。XElement.Load()返回的对象是XML的根节点,如果你要找的<item>节点不是根节点的直接子节点(比如XML存在嵌套层级、或者结构不规范导致层级错乱),调用Elements("item")就匹配不到结果;而Descendants()方法会递归遍历当前节点下所有层级的后代节点,所以可以找到目标节点。- 给出的示例XML存在结构错误:末尾根节点写的是开标签
<items>,没有用闭标签</items>闭合,会导致XML解析时层级错乱,直接影响节点匹配结果。
修复方式:
- 先修正XML结构错误,补全根节点的闭合标签,保证XML格式合法。
- 不确定节点层级时,直接用
Descendants("item")代替Elements("item")做递归查询,兼容不同层级的节点查找场景。 - 如果XML声明了默认命名空间,查询时需要传入带对应命名空间的XName,否则也会匹配不到节点。
问题2:读取media:content节点抛出冒号非法异常
核心原因:
media:是XML命名空间前缀,不是节点名称的一部分,冒号是前缀和节点本地名的分隔符,直接把"media:content"作为节点名传入查询方法时,LINQ to XML会把整个字符串当做节点名称处理,自然会抛出名称不能包含冒号的异常。- 示例XML中使用了
media:前缀,但没有在根节点声明前缀对应的命名空间地址,本身就是不合法的XML结构。RSS场景下media前缀对应的标准命名空间为http://search.yahoo.com/mrss/。
修复方式:
- 先在XML根节点补上media前缀的命名空间声明,修正后的合法XML结构如下:
<items xmlns:media="http://search.yahoo.com/mrss/"> <item> <title>Weź udział w grze historycznej</title> <link>https://legionowo.pl/a/wez-udzial-w-grze-historycznej</link> <description><![CDATA[<img src="https://legionowo.pl/img/artykuly/4/2022_07/x41594.jpg.pagespeed.ic.9BJkaqvzdK.jpg"><br><br>]]></description> <pubDate>Fri, 01 Jul 2022 09:10:50 +0000</pubDate> <media:content url="https://legionowo.pl/img/artykuly/4/2022_07/x41594.jpg.pagespeed.ic.9BJkaqvzdK.jpg" medium="image" /> <guid isPermaLink="false">https://legionowo.pl/a/wez-udzial-w-grze-historycznej</guid> </item> </items>
- 查询带命名空间前缀的节点时,先声明对应的
XNamespace对象,通过命名空间对象 + 节点本地名的方式构造查询条件,修复后的完整读取代码如下:
private async Task GetXmlData() { // 声明media前缀对应的命名空间,必须和XML中声明的地址完全一致 XNamespace mediaNs = "http://search.yahoo.com/mrss/"; XElement doc = XElement.Load("C:\\mail.xml"); // 递归查询所有item节点 List<XElement> itemList = doc.Descendants("item").ToList(); foreach(XElement item in itemList) { Console.WriteLine($"标题:{item.Element("title")?.Value}"); // 查询media:content节点 XElement mediaContentNode = item.Element(mediaNs + "content"); if (mediaContentNode != null) { // 读取节点属性 string imgUrl = mediaContentNode.Attribute("url")?.Value; string mediaType = mediaContentNode.Attribute("medium")?.Value; Console.WriteLine($"配图地址:{imgUrl},媒体类型:{mediaType}"); } } }
注意点
- 只要XML节点带命名空间前缀,查询时都不能直接写带冒号的完整名称,必须通过
XNamespace构造合法的XName做匹配。 - 使用XML前缀时必须提前声明对应的命名空间地址,否则XML本身格式不合法,会触发各类解析异常。
内容的提问来源于stack exchange,提问作者Software_Architect
相关产品推荐
相关产品推荐

