Подтвердить что ты не робот

SelectSingleNode возвращает значение null для известного пути xml node с использованием XPath

Рассмотрим этот простой XML-документ. Сериализованный XML, показанный здесь, является результатом XmlSerializer из сложного объекта POCO, чью схему я не контролирую.

<My_RootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="">
  <id root="2.16.840.1.113883.3.51.1.1.1" extension="someIdentifier" xmlns="urn:hl7-org:v3" /> 
  <creationTime xsi:nil="true" xmlns="urn:hl7-org:v3" />      
</My_RootNode>

Цель состоит в том, чтобы извлечь значение атрибута расширения на id node. В этом случае мы используем метод SelectSingleNode и получаем выражение XPath как таковое:

XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/id");
//idNode is evaluated to null at this point in the debugger!
string msgID = idNode.Attributes.GetNamedItem("extension").Value;

Проблема заключается в том, что метод SelectSingleNode возвращает значение null для данного выражения XPath.

Вопрос: любые идеи по правильности запроса XPath или почему этот метод вызывает выражение + XPath, возвращающее нулевое значение? Возможно, пространства имен являются частью проблемы?

4b9b3361

Ответ 1

Я сильно подозреваю, что проблема связана с пространствами имен. Попытайтесь избавиться от пространства имен, и все будет хорошо, но, очевидно, это не поможет в вашем реальном случае, когда я предполагаю, что документ исправлен.

Я не могу вспомнить, как указать пространство имен в выражении XPath, но я уверен, что проблема.

EDIT: Хорошо, я вспомнил, как это сделать сейчас. Это не очень приятно, хотя - вам нужно создать XmlNamespaceManager для него. Вот пример кода, который работает с вашим примером документа:

using System;
using System.Xml;

public class Test
{
    static void Main()
    {
        XmlDocument doc = new XmlDocument();
        XmlNamespaceManager namespaces = new XmlNamespaceManager(doc.NameTable);
        namespaces.AddNamespace("ns", "urn:hl7-org:v3");
        doc.Load("test.xml");
        XmlNode idNode = doc.SelectSingleNode("/My_RootNode/ns:id", namespaces);
        string msgID = idNode.Attributes["extension"].Value;
        Console.WriteLine(msgID);
    }
}

Ответ 2

Если вы хотите полностью игнорировать пространства имен, вы можете использовать это:

static void Main(string[] args)
{
    string xml =
        "<My_RootNode xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"\">\n" +
        "    <id root=\"2.16.840.1.113883.3.51.1.1.1\" extension=\"someIdentifier\" xmlns=\"urn:hl7-org:v3\" />\n" +
        "    <creationTime xsi:nil=\"true\" xmlns=\"urn:hl7-org:v3\" />\n" +
        "</My_RootNode>";

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);

    XmlNode idNode = doc.SelectSingleNode("/*[local-name()='My_RootNode']/*[local-name()='id']");
}

Ответ 3

Извините, вы забыли пространство имен. Вам нужно:

XmlNamespaceManager ns = new XmlNamespaceManager(myXmlDoc.NameTable);
ns.AddNamespace("hl7","urn:hl7-org:v3");
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/hl7:id", ns);

Фактически, будь то здесь или в веб-службах, возвращая null из операции XPath или что-то, что зависит от XPath, обычно указывает на проблему с пространствами имен XML.

Ответ 4

Это должно работать в вашем случае без удаления пространств имен:

XmlNode idNode = myXmlDoc.GetElementsByTagName("id")[0];

Ответ 5

Ну... У меня была такая же проблема, и это была головная боль. Поскольку я не очень интересовался пространством имен или схемой xml, я просто удалил эти данные из своего xml и решил все мои проблемы. Может быть, не лучший ответ? Возможно, но если вы не хотите иметь дело со всем этим, и вы ТОЛЬКО заботитесь о данных (и не будете использовать xml для какой-либо другой задачи), удаление пространства имен может решить ваши проблемы.

XmlDocument vinDoc = new XmlDocument();
string vinInfo = "your xml string";
vinDoc.LoadXml(vinInfo);

vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=\"http://tempuri.org\/\", "");

Ответ 6

Чтобы решить проблемы с пространством имен, в моем случае я работал с документами с несколькими пространствами имен и нуждался в правильном обращении с пространствами имен. Я написал функцию ниже, чтобы получить диспетчер пространства имен для обработки любого пространства имен в документе:

private XmlNamespaceManager GetNameSpaceManager(XmlDocument xDoc)
    {
        XmlNamespaceManager nsm = new XmlNamespaceManager(xDoc.NameTable);
        XPathNavigator RootNode = xDoc.CreateNavigator();
        RootNode.MoveToFollowing(XPathNodeType.Element);
        IDictionary<string, string> NameSpaces = RootNode.GetNamespacesInScope(XmlNamespaceScope.All);

        foreach (KeyValuePair<string, string> kvp in NameSpaces)
        {
            nsm.AddNamespace(kvp.Key, kvp.Value);
        }

        return nsm;
    }

Ответ 7

просто используйте //id вместо/id. Он отлично работает в моем коде

Ответ 8

Правило, о котором следует помнить: если ваш документ указывает namespace, вы должны использовать XmlNamespaceManager в своем обращении к SelectNodes() или SelectSingleNode(). Это хорошо.

См. статью Преимущества пространств имен. Джон Скит отлично справляется с ответом, показывающим, как использовать XmlNamespaceManager. (Этот ответ должен быть просто комментарием к этому ответу, но у меня недостаточно очков для комментариев.)

Ответ 9

Ответ Roisgoen работал у меня, но чтобы сделать его более общим, вы можете использовать RegEx:

//Substitute "My_RootNode" for whatever your root node is
string strRegex = @"<My_RootNode(?<xmlns>\s+xmlns([\s]|[^>])*)>";
var myMatch = new Regex(strRegex, RegexOptions.None).Match(myXmlDoc.InnerXml);
if (myMatch.Success)
{
    var grp = myMatch.Groups["xmlns"];
    if (grp.Success)
    {
        myXmlDoc.InnerXml = myXmlDoc.InnerXml.Replace(grp.Value, "");
    }
}

Я полностью признаю, что это не лучший ответ, но это легко исправить, а иногда и все, что нам нужно.