read - xpath with namespace c#
SelectSingleNode devuelve nulo para una buena ruta conocida de nodo xml usando XPath (9)
Considere este simple documento XML. El XML serializado que se muestra aquí es el resultado de un XmlSerializer de un objeto POCO complejo cuyo esquema no tengo control.
<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>
El objetivo es extraer el valor del atributo de extensión en el nodo de id. En este caso, estamos utilizando el método SelectSingleNode y se le ha dado una expresión XPath como tal:
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;
El problema es que el método SelectSingleNode
devuelve null para la expresión XPath dada.
Pregunta: ¿ alguna idea sobre la corrección de esta consulta XPath, o por qué este método llama a la expresión + XPath devolvería un valor nulo? Tal vez los espacios de nombres son parte del problema?
Bueno ... tuve el mismo problema y fue un dolor de cabeza. Como no me importaba demasiado el espacio de nombres o el esquema xml, simplemente borré estos datos de mi xml y resolvió todos mis problemas. Puede que no sea la mejor respuesta? Probablemente, pero si no quiere ocuparse de todo esto y SÓLO le importan los datos (y no usará el xml para otra tarea), eliminar el espacio de nombres puede resolver sus problemas.
XmlDocument vinDoc = new XmlDocument();
string vinInfo = "your xml string";
vinDoc.LoadXml(vinInfo);
vinDoc.InnerXml = vinDoc.InnerXml.Replace("xmlns=/"http://tempuri.org///", "");
Esto debería funcionar en su caso sin eliminar los espacios de nombres:
XmlNode idNode = myXmlDoc.GetElementsByTagName("id")[0];
La regla a tener en cuenta es: si su documento especifica un namespace
, DEBE usar un XmlNamespaceManager
en su llamada a SelectNodes()
o SelectSingleNode()
. Eso es bueno.
Ver el artículo Ventajas de los espacios de nombres . Jon Skeet hace un gran trabajo en su respuesta que muestra cómo usar XmlNamespaceManager
. (Esta respuesta debería ser simplemente un comentario sobre esa respuesta, pero no tengo suficientes puntos de referencia para comentar).
La respuesta de Roisgoen funcionó para mí, pero para hacerlo más general, puede usar un 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, "");
}
}
Admito plenamente que esta no es una respuesta de mejores prácticas, pero es una solución fácil y, a veces, eso es todo lo que necesitamos.
Lo siento, olvidaste el espacio de nombres. Necesitas:
XmlNamespaceManager ns = new XmlNamespaceManager(myXmlDoc.NameTable);
ns.AddNamespace("hl7","urn:hl7-org:v3");
XmlNode idNode = myXmlDoc.SelectSingleNode("/My_RootNode/hl7:id", ns);
De hecho, ya sea aquí o en servicios web, obtener un valor nulo de una operación XPath o cualquier cosa que dependa de XPath generalmente indica un problema con los espacios de nombres XML.
Si desea ignorar por completo los espacios de nombres, puede usar esto:
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'']");
}
Solo para construir sobre la solución de los problemas del espacio de nombres, en mi caso me he encontrado con documentos con varios espacios de nombres y necesitaba manejar espacios de nombres correctamente. Escribí la siguiente función para obtener un administrador de espacio de nombre para tratar con cualquier espacio de nombre en el documento:
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;
}
Sospecho fuertemente que el problema tiene que ver con espacios de nombres. Intenta deshacerte del espacio de nombres y estarás bien, pero obviamente eso no ayudará en tu caso real, en el que asumiría que el documento está arreglado.
No puedo recordar cómo especificar un espacio de nombres en una expresión XPath, pero estoy seguro de que ese es el problema.
EDITAR: Bien, he recordado cómo hacerlo ahora. Aunque no es terriblemente agradable, necesitas crear un XmlNamespaceManager
para ello. Aquí hay un código de muestra que funciona con su documento de muestra:
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);
}
}
solo use // id en lugar de / id. Funciona bien en mi código