C#通过XPath读取带命名空间WebEx XML节点失败问题排查
问题根因
你的代码有3处核心错误,导致节点查询全部失败:
- 命名空间注册错误:你注册的
sess前缀对应的URI和XML实际定义不匹配,还漏注册了com前缀的命名空间。原XML中sess的命名空间地址为http://www.webex.com/schemas/2002/06/service/session,你代码里少了路径中的/service段,直接导致所有带sess前缀的节点匹配失败;另外person节点下的姓名、邮箱、用户类型等字段都属于com命名空间,你没有注册这个前缀,后续读取这类字段会直接返回null。 - XPath语法笔误:查询person节点的路径中写了
serv::bodyContent,前缀和节点名之间错误使用了双冒号,正确写法为单冒号serv:bodyContent。双冒号是XPath轴的专用语法,这里属于书写错误。 - 相对路径使用逻辑错误:当你已经拿到
attendeesNode子节点对象后,如果传入SelectNodes的XPath以/开头,查询会强制从整个XML文档的根节点开始执行,不会限定在当前节点的子级范围内查找;而且你已经定位到attendees节点,完全不需要重复写从根节点开始的全路径。
修改方案
首先修正命名空间注册逻辑,补全错误的URI和缺失的命名空间,注意命名空间管理器只需要初始化一次即可,不需要每次查询都重复创建:
private XmlNamespaceManager GetNameSpace(XmlNameTable objNameTable) { XmlNamespaceManager objNsManager = new XmlNamespaceManager(objNameTable); objNsManager.AddNamespace("serv", "http://www.webex.com/schemas/2002/06/service"); // 补全缺失的com命名空间 objNsManager.AddNamespace("com", "http://www.webex.com/schemas/2002/06/common"); // 修正sess命名空间URI,补全缺失的/service路径段 objNsManager.AddNamespace("sess", "http://www.webex.com/schemas/2002/06/service/session"); objNsManager.AddNamespace("train", "http://www.webex.com/schemas/2002/06/service/trainingsession"); // 其余自定义命名空间如果后续业务需要可保留,不影响当前查询 objNsManager.AddNamespace("ns1", "http://www.webex.com/schemas/2002/06/service/site"); objNsManager.AddNamespace("body", "http://www.webex.com/schemas/2002/06/service/training"); objNsManager.AddNamespace("ep", "http://www.webex.com/schemas/2002/06/service/ep"); objNsManager.AddNamespace("meet", "http://www.webex.com/schemas/2002/06/service/meeting"); return objNsManager; }
节点查询部分的修正代码如下,已实现从已获取的attendees节点直接查询子级person节点,不需要写全路径:
// 初始化一次命名空间管理器即可 var nsMgr = GetNameSpace(xmlDoc.NameTable); // 查询会议密码节点 var sessionPasswordNode = xmlDoc.SelectSingleNode("/serv:message/serv:body/serv:bodyContent/sess:accessControl/sess:sessionPassword", nsMgr); string sessionPwd = sessionPasswordNode?.InnerText; // 可获取到密码值12345678 // 查询参会人根节点 var attendeesNode = xmlDoc.SelectSingleNode("/serv:message/serv:body/serv:bodyContent/train:attendees", nsMgr); // 从当前attendeesNode节点范围查询子级person节点,使用.开头的相对路径 // .// 表示递归查找当前节点下所有层级的匹配节点,不需要严格对齐中间层级 var personNodes = attendeesNode.SelectNodes(".//sess:person", nsMgr); foreach (XmlNode person in personNodes) { // 读取com命名空间下的字段值 string userName = person.SelectSingleNode("com:name", nsMgr)?.InnerText; string email = person.SelectSingleNode("com:email", nsMgr)?.InnerText; string userType = person.SelectSingleNode("com:type", nsMgr)?.InnerText; // 其余字段按相同逻辑读取即可 }
相对路径写法说明:
- 如果明确知道节点的层级关系,可以直接写严格层级路径,比如
SelectNodes("sess:participants/sess:participant/sess:person", nsMgr),只会匹配当前节点下严格按层级嵌套的目标节点- 如果不需要严格匹配中间层级,用
.//开头的写法即可,比如.//sess:person会递归查找当前节点下所有位置的sess:person节点,写法更简洁
内容的提问来源于stack exchange,提问作者sunil20000
相关产品推荐
相关产品推荐

