2010-04-09 18 views
62

¿Hay alguna manera 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 elementos basados ​​en el nombre. Algo así como, dame todos los elementos donde el nombre es CreditCard. No me importa lo que es el espacio de nombres.Buscar XDocument utilizando LINQ sin conocer el espacio de nombres

Mi problema parece ser con LINQ y que 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"); 

que realmente quiere tener la capacidad de buscar XML sin necesidad de 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 va a funcionar porque no sé de antemano el espacio de nombres en tiempo de 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>  
     ... 
+0

Echa un vistazo a esta respuesta de otra pregunta: http://stackoverflow.com/questions/934486/how-do-i-get-a-nametable-f-an-xdocument/3977823#3977823 – MonkeyWrench

Respuesta

71

Como Adam precises en el comentario, XName son convertibles a una cadena, pero esa cadena requiere el espacio de nombres cuando hay uno. Es por eso que la comparación de .Name a una cadena falla, o por qué no se puede pasar "Persona" como parámetro al Método XLinq para filtrar su nombre.
XName se compone de un prefijo (el Namespace) y un LocalName. El nombre local es lo que quiere consultar si está ignorando espacios de nombres.
Gracias :) Adam

No se puede poner el nombre del nodo como un parámetro del método .Descendants(), pero se puede consultar 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>"); 

EDIT:mala copia/más allá 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); 
} 

que funciona para mí ...

+3

Podría ayudar a dar una explicación de * por qué * su respuesta es la forma en que es: Name es un XName, y XName simplemente es convertible a una cadena, por lo que la comparación de .Name a una cadena falla con la pregunta asker's query . XName consta de un prefijo y un nombre local, y el nombre local es el que desea consultar si ignora los espacios de nombres. –

+0

que estaba en el comentario que he puesto en la respuesta somerockstar. Puedo agregar esto para mayor claridad, tienes razón –

+0

Muchas gracias por la ayuda rápida. Espero que esto ayude a alguien más. –

-6

sólo tiene que utilizar el método descendientes:

XDocument doc = XDocument.Load(filename); 
String[] creditCards = (from creditCardNode in doc.Root.Descendents("CreditCardNumber") 
         select creditCardNode.Value).ToArray<string>(); 
+2

que no funcionará ya que el parámetro descendiente solicita un XName, y el XName está prefijado por un espacio de nombres aquí. –

8

Creo que encontré lo que estaba buscando. 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 si es una mejor 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 encriptar los valores.

Si alguien tiene una solución mejor, por favor proporciónela. Gracias.

2

Si sus documentos XML siempre define el espacio de nombres en el mismo nodo (Request nodo en los dos ejemplos dados), se puede determinar que al hacer una consulta y ver lo que los espacios de nombres el resultado tiene:

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; 
} 
76

Usted podría tomar el espacio de nombres de la raíz-elemento:

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml"); 
var ns = xDocument.Root.Name.Namespace; 

Ahora usted puede conseguir todos los elementos deseados fácilmente usando el plus-operador:

root.Elements(ns + "CreditCardNumber") 
+0

Esta parece ser la mejor respuesta, ya que todavía le permite usar la mayoría de las operaciones 'LINQ'. –

+1

Esta respuesta solo es aceptable si ninguno de los elementos está en un espacio de nombre diferente al del documento raíz. Sí, es fácil saber el espacio de nombres si solo le pides el documento raíz, pero es más complicado pedir cualquier elemento de un nombre dado, independientemente del espacio de nombres en el que esté el elemento. Es por eso que considero que el uso de las respuestas XElement.Name.LocalName (generalmente a través de linq) son más generalizados. –

Cuestiones relacionadas