¿Hay alguna forma de buscar un XDocument sin conocer el espacio de nombres? Tengo un proceso que registra todas las solicitudes SOAP y encripta los datos confidenciales. Quiero encontrar cualquier elemento basado en el nombre. Algo como, dame todos los elementos donde el nombre es CreditCard. No me importa cuál sea el espacio de nombres.
Mi problema parece estar en LINQ y requiere un espacio de nombres xml.
Tengo otros procesos que recuperan valores de XML, pero conozco el espacio de nombres para estos otros procesos.
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
XNamespace xNamespace = "http://CompanyName.AppName.Service.Contracts";
var elements = xDocument.Root
.DescendantsAndSelf()
.Elements()
.Where(d => d.Name == xNamespace + "CreditCardNumber");
Realmente quiero tener la capacidad de buscar xml sin conocer los espacios de nombres, algo como esto:
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root
.DescendantsAndSelf()
.Elements()
.Where(d => d.Name == "CreditCardNumber")
Esto no funcionará porque no conozco el espacio de nombres de antemano en el momento de la compilación.
¿Cómo se puede hacer esto?
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Request xmlns="http://CompanyName.AppName.Service.ContractA">
<Person>
<CreditCardNumber>83838</CreditCardNumber>
<FirstName>Tom</FirstName>
<LastName>Jackson</LastName>
</Person>
<Person>
<CreditCardNumber>789875</CreditCardNumber>
<FirstName>Chris</FirstName>
<LastName>Smith</LastName>
</Person>
...
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Request xmlns="http://CompanyName.AppName.Service.ContractsB">
<Transaction>
<CreditCardNumber>83838</CreditCardNumber>
<TransactionID>64588</FirstName>
</Transaction>
...
fuente
Respuestas:
Como Adam precisa en el comentario, XName se puede convertir en una cadena, pero esa cadena requiere el espacio de nombres cuando hay uno. Es por eso que la comparación de .Name con una cadena falla, o por qué no puede pasar "Persona" como parámetro al Método XLinq para filtrar por su nombre.
XName consta de un prefijo (el espacio de nombres) y un LocalName. El nombre local es lo que desea consultar si ignora los espacios de nombres.
Gracias Adam :)
No puede poner el Nombre del nodo como parámetro del método .Descendants (), pero puede consultar de esa manera:
var doc= XElement.Parse( @"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/""> <s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""> <Request xmlns=""http://CompanyName.AppName.Service.ContractA""> <Person> <CreditCardNumber>83838</CreditCardNumber> <FirstName>Tom</FirstName> <LastName>Jackson</LastName> </Person> <Person> <CreditCardNumber>789875</CreditCardNumber> <FirstName>Chris</FirstName> <LastName>Smith</LastName> </Person> </Request> </s:Body> </s:Envelope>");
EDITAR: mala copia / pasado de mi prueba :)
var persons = from p in doc.Descendants() where p.Name.LocalName == "Person" select p; foreach (var p in persons) { Console.WriteLine(p); }
Funciona para mi...
fuente
Puede tomar el espacio de nombres del elemento raíz:
XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); var ns = xDocument.Root.Name.Namespace;
Ahora puede obtener todos los elementos deseados fácilmente usando el operador más:
root.Elements(ns + "CreditCardNumber")
fuente
LINQ
operaciones.Creo que encontré lo que buscaba. Puedes ver en el siguiente código que hago la evaluación
Element.Name.LocalName == "CreditCardNumber"
. Esto pareció funcionar en mis pruebas. No estoy seguro de si es una buena práctica, pero la usaré.XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); var elements = xDocument.Root.DescendantsAndSelf().Elements().Where(d => d.Name.LocalName == "CreditCardNumber");
Ahora tengo elementos donde puedo cifrar los valores.
Si alguien tiene una mejor solución, por favor proporcione. Gracias.
fuente
Si sus documentos XML siempre definen el espacio de nombres en el mismo nodo (
Request
nodo en los dos ejemplos dados), puede determinarlo haciendo una consulta y viendo qué espacio de nombres tiene el resultado:XDocument xDoc = XDocument.Load("filename.xml"); //Initial query to get namespace: var reqNodes = from el in xDoc.Root.Descendants() where el.Name.LocalName == "Request" select el; foreach(var reqNode in reqNodes) { XNamespace xns = reqNode.Name.Namespace; //Queries making use of namespace: var person = from el in reqNode.Elements(xns + "Person") select el; }
fuente
Hay un par de respuestas con métodos de extensión que se han eliminado. No estoy seguro de por qué. Aquí está mi versión que funciona para mis necesidades.
public static class XElementExtensions { public static XElement ElementByLocalName(this XElement element, string localName) { return element.Descendants().FirstOrDefault(e => e.Name.LocalName == localName && !e.IsEmpty); } }
El
IsEmpty
es filtrar nodos conx:nil="true"
Puede haber sutilezas adicionales, así que utilícelo con precaución.
fuente
Simplemente use el método Descendents:
XDocument doc = XDocument.Load(filename); String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber") select creditCardNode.Value).ToArray<string>();
fuente