84 votes

Recherche de XDocument à l'aide de LINQ sans connaître l'espace de noms

Existe-t-il un moyen d'effectuer une recherche dans un XDocument sans connaître l'espace de noms ? J'ai un processus qui enregistre toutes les demandes SOAP et crypte les données sensibles. Je veux trouver tous les éléments basés sur le nom. Quelque chose comme : donnez-moi tous les éléments dont le nom est CreditCard. Je ne me soucie pas de l'espace de noms.

Mon problème semble être lié à LINQ et à la nécessité d'un espace de nom xml.

J'ai d'autres processus qui récupèrent des valeurs à partir de XML, mais je connais l'espace de nom de ces autres processus.

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");

Je veux vraiment avoir la possibilité de rechercher du xml sans connaître les espaces de noms, quelque chose comme ceci :

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root
                        .DescendantsAndSelf()
                        .Elements()
                        .Where(d => d.Name == "CreditCardNumber")

Cela ne fonctionnera pas car je ne connais pas l'espace de noms au préalable lors de la compilation.

Comment cela peut-il être fait ?

<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>      
        ...

96voto

Stephane Points 4258

Comme Adam le précise dans le commentaire, les XName sont convertibles en une chaîne de caractères, mais cette chaîne nécessite l'espace de noms lorsqu'il y en a un. C'est pourquoi la comparaison de .Name à une chaîne de caractères échoue, ou pourquoi vous ne pouvez pas passer "Personne" comme paramètre à la méthode XLinq pour filtrer sur leur nom.
XName se compose d'un préfixe (l'espace de nommage) et d'un LocalName. Le nom local est ce sur quoi vous voulez faire une requête si vous ignorez les espaces de noms.
Merci Adam :)

Vous ne pouvez pas mettre le nom du nœud comme paramètre de la méthode .Descendants(), mais vous pouvez faire une requête de cette façon :

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 : mauvais copier/coller de mon test :)

var persons = from p in doc.Descendants()
              where p.Name.LocalName == "Person"
              select p;

foreach (var p in persons)
{
    Console.WriteLine(p);
}

Ça marche pour moi...

92voto

Sascha Points 826

Vous pourriez prendre l'espace de nom de l'élément racine :

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

Vous pouvez maintenant obtenir facilement tous les éléments souhaités en utilisant l'opérateur plus :

root.Elements(ns + "CreditCardNumber")

15voto

BarDev Points 3304

Je pense avoir trouvé ce que je cherchais. Vous pouvez voir dans le code suivant que je fais l'évaluation Element.Name.LocalName == "CreditCardNumber" . Cela semble fonctionner dans mes tests. Je ne sais pas si c'est une bonne pratique, mais je vais l'utiliser.

XDocument xDocument = XDocument.Load(@"C:\temp\Packet.xml");
var elements = xDocument.Root.DescendantsAndSelf().Elements().Where(d => d.Name.LocalName == "CreditCardNumber");

Maintenant, j'ai des éléments où je peux crypter les valeurs.

Si quelqu'un a une meilleure solution, veuillez la fournir. Merci.

2voto

Clivest Points 152

Si vos documents XML définissent toujours l'espace de nom dans le même nœud ( Request dans les deux exemples donnés), vous pouvez le déterminer en effectuant une requête et en voyant quel est l'espace de nom du résultat :

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

-7voto

Evan Chiu Points 90

Il suffit d'utiliser la méthode Descendents :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X