【问题标题】:XmlNodeList emptyXmlNodeList 为空
【发布时间】:2015-02-02 15:51:52
【问题描述】:

我得到了这个 xml。

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
   <soapenv:Header/>
   <soapenv:Body>
      <GetSelectedModels xmlns="http://www.something/">
         <!--Optional:-->
         <input xmlns="http://www.something/">
            <X_Models>
            <ListOfModels>
               <Model>
                  <ModelId>163</ModelId>
               </Model>             
            </ListOfModels>
         </X_Models>
         </input>
      </GetSelectedModels>
   </soapenv:Body>
</soapenv:Envelope>

当我在 SoapUi 中发布它并到达我的网络服务时,我想使用节点。

XmlDocument docRequest = new XmlDocument();
docRequest.LoadXml(xml);
XmlNodeList models= docRequest.SelectNodes("X_Models/ListOfModels/Model");

models 为 0,xml 如下所示:

 <X_Modelsxmlns="http://www.something/">
            <ListOfModels>
               <Model>
                  <ModelID>163</ModelID>
               </Model>             
            </ListOfModelss>
         </X_Models>

如果我像这样使用别名发布我的 xml:

 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:net="http://www.something/" >
       <soapenv:Header/>
       <soapenv:Body>
          <net:GetSelectedModels>
             <!--Optional:-->
             <net:input>
                <X_Models>
                <ListOfModels>
                   <Model>
                      <ModelId>163</ModelId>
                   </Model>             
                </ListOfModels>
             </X_Models>
             </net:input>
          </net:GetSelectedModels>
       </soapenv:Body>
    </soapenv:Envelope>

那么模型将为 1,xml 如下所示:

 <X_Models>
 <ListOfModels>
                    <Model>
                          <ModelID>163</ModelID>
                       </Model>             
                    </ListOfModelss>
                 </X_Models>

谁能指出我正确的方向?谢谢!

【问题讨论】:

    标签: c# xml web-services


    【解决方案1】:

    在第一种情况下,X_Models 节点和所有后代都在默认命名空间中,即xmlns="http://www.something/"。在第二种情况下,没有默认命名空间,因此X_Models 树不在任何命名空间中。

    XmlNode.SelectNodes Method (String) 的文档指定必须按如下方式处理默认命名空间:

    如果 XPath 表达式不包含前缀,则假定名称空间 URI 是空名称空间。如果您的 XML 包含默认命名空间,您仍必须使用 XmlNamespaceManager 并向其添加前缀和命名空间 URI;否则,您将不会选择任何节点。如需更多信息,请参阅Select Nodes Using XPath Navigation

    因此,鉴于您在问题开始时显示的 XML(与您的代码不太匹配),您需要这样做:

        string xml = @"<?xml version=""1.0"" standalone=""no"" ?>
            <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" >
               <soapenv:Header/>
               <soapenv:Body>
                  <GetSelectedModels xmlns=""http://www.something/"">
                     <!--Optional:-->
                     <input xmlns=""http://www.something/"">
                        <X_Models>
                        <ListOfModels>
                           <Model>
                              <ModelId>163</ModelId>
                           </Model>             
                        </ListOfModels>
                     </X_Models>
                     </input>
                  </GetSelectedModels>
               </soapenv:Body>
            </soapenv:Envelope>
            ";
    
        XmlDocument docRequest = new XmlDocument();
        docRequest.LoadXml(xml);
    
        XmlNamespaceManager ns = new XmlNamespaceManager(docRequest.NameTable);
        ns.AddNamespace("models", "http://www.something/");
        ns.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
    
        XmlNodeList models = docRequest.SelectNodes("/soapenv:Envelope/soapenv:Body/models:GetSelectedModels/models:input/models:X_Models/models:ListOfModels/models:Model", ns);
        Debug.Assert(models.Count == 1);
    

    更新

    如果您真的想在忽略名称空间并仅使用本地名称的 XML 层次结构上运行查询,那么SelectNodes 不会开箱即用。您需要几个扩展方法,例如:

    public static class XmlNodeExtensions
    {
        public static IEnumerable<XmlElement> ChildElements(this IEnumerable<XmlElement> elements)
        {
            return elements.SelectMany(e => e.ChildNodes.OfType<XmlElement>());
        }
    
        public static IEnumerable<XmlElement> OfLocalName(this IEnumerable<XmlElement> elements, string localName)
        {
            return elements.Where(e => e.LocalName == localName);
        }
    }
    

    然后你可以这样做:

        string xml1 = @"<?xml version=""1.0"" standalone=""no"" ?>
            <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" >
               <soapenv:Header/>
               <soapenv:Body>
                  <GetSelectedModels xmlns=""http://www.something/"">
                     <!--Optional:-->
                     <input xmlns=""http://www.something/"">
                        <X_Models>
                        <ListOfModels>
                           <Model>
                              <ModelId>163</ModelId>
                           </Model>             
                        </ListOfModels>
                     </X_Models>
                     </input>
                  </GetSelectedModels>
               </soapenv:Body>
            </soapenv:Envelope>
            ";
    
        string xml2 = @"
            <soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:net=""http://www.something/"" >
               <soapenv:Header/>
               <soapenv:Body>
                  <net:GetSelectedModels>
                     <!--Optional:-->
                     <net:input>
                        <X_Models>
                        <ListOfModels>
                           <Model>
                              <ModelId>163</ModelId>
                           </Model>             
                        </ListOfModels>
                     </X_Models>
                     </net:input>
                  </net:GetSelectedModels>
               </soapenv:Body>
            </soapenv:Envelope>
            ";
    
        XmlDocument doc1 = new XmlDocument();
        doc1.LoadXml(xml1);
    
        XmlDocument doc2 = new XmlDocument();
        doc2.LoadXml(xml2);
    
        var ignoreNameSpaceModels1 = doc1.DocumentElement.ChildNodes.OfType<XmlElement>().OfLocalName("Body").ChildElements().OfLocalName("GetSelectedModels").ChildElements().OfLocalName("input").ChildElements().OfLocalName("X_Models").ToArray();
        var ignoreNameSpaceModels2 = doc2.DocumentElement.ChildNodes.OfType<XmlElement>().OfLocalName("Body").ChildElements().OfLocalName("GetSelectedModels").ChildElements().OfLocalName("input").ChildElements().OfLocalName("X_Models").ToArray();
    
        Debug.Assert(ignoreNameSpaceModels1.Length == 1 && ignoreNameSpaceModels2.Length == 1 && ignoreNameSpaceModels1[0].LocalName == "X_Models" && ignoreNameSpaceModels2[0].LocalName == "X_Models");
    

    但这就像说typeof(System.Windows.Forms.RichTextBox) == typeof(System.Windows.Controls.RichTextBox) 因为他们的Name 是一样的。这是因为,xmlns="http://www.something/" 属性如下:

          <GetSelectedModels xmlns="http://www.something/">
              <!--child elements... -->
          </GetSelectedModels>
    

    表示“元素GetSelectedModels和所有子元素默认属于http://www.something/命名空间”。

    中的net: 前缀
          <net:GetSelectedModels>
              <!--child elements... -->
          </net:GetSelectedModels>
    

    仅表示“元素GetSelectedModels 属于命名空间http://www.something/”。它不适用于子元素。递归作用域是default namespace attribute 的一个特殊属性。

    【讨论】:

    • 谢谢,但是当我运行它时,我仍然得到 0 个模型。 (如果我使用 xml 和 XPath 运行 XPath 测试器,它会说尚未声明带有前缀“models”的命名空间。)我需要第一个示例像第二个示例一样工作,但没有别名。
    • @user3436977 - 我已经更新了答案以显示我用来通过代码测试的 XML。您的问题显示了几个 XML 示例,但不清楚您正在使用哪个示例进行测试。您的最终 XML 示例与您的第一个示例不等效,因为 X_Models 子树位于不同的 XML namespaces 中。
    • 如果我运行您的示例,它将起作用。问题是,当我将 xml 发布到我的 Web 服务(使用soapui)时,作为输入的 xml 不会。
    • @user3436977 - 我的例子来自你的问题。您想使用哪个示例?
    • 发布的 xml 是您使用的 xml,但我在需要处理的 Web 服务中获得的 xml 是:something"> 163
    猜你喜欢
    • 2010-12-08
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    相关资源
    最近更新 更多