web-dev-qa-db-fra.com

Xml Namespace briser mon xpath!

J'ai le XML suivant:

<List xmlns="http://schemas.Microsoft.com/sharepoint/soap/">
 <Fields>
   <Field>
   </Field>
 </Fields>
</List>

Il s'agit d'une version allégée de XML renvoyée par un service Web SharePoint. J'ai aussi le xPath suivant:

/List/Fields/Field

Lorsque je supprime le xmlns de mon XML, xPath fonctionne correctement. Quand il est dedans, mon xPath ne trouve rien. Y a-t-il quelque chose que je devrais faire différemment avec mon xPath? La modification du XML n'est pas une option.

80
Abe Miessler

J'ai aussi le xPath suivant:

/List/Fields/Field 

Lorsque je supprime les xmlns de mon XML, xPath fonctionne correctement. Quand il est dedans mon xPath ne trouve rien

Si vous ne pouvez pas enregistrer une liaison d'espace de noms et ne pouvez pas utiliser (en supposant que le préfixe enregistré est "x"):

/x:List/x:Fields/x:Field

alors il y a un autre moyen:

/*[name()='List']/*[name()='Fields']/*[name()='Field']
102

L'élément List a été défini avec un espace de nom par défaut, qui est adopté par tous les éléments qu'il contient.

Vous devez donc ignorer l'espace de nom d'élément comme suit:

/*[local-name()='List']/*[local-name()='Fields]/*[local-name()='Field]

mais cela signifie que le xpath prendra n'importe quel autre élément avec List - Fields - Field

Vous pouvez effectuer une vérification d'espace de nom ainsi qu'une vérification de nom local, comme suit:

/*[local-name()='List' and namespace-uri()='http://schemas.Microsoft.com/sharepoint/soap/']/*[local-name()='Fields' and namespace-uri()='http://schemas.Microsoft.com/sharepoint/soap/']/*[local-name()='Field' and namespace-uri()='http://schemas.Microsoft.com/sharepoint/soap/']

Ou vous pouvez enregistrer l'espace de noms avec la bibliothèque, puis spécifier explicitement le préfixe de cet espace de noms et l'ajouter à l'expression xpath, dont la méthode dépend de la bibliothèque que vous utilisez.

44
rogermushroom

Vous devrez probablement enregistrer cet espace de noms URI avec votre bibliothèque xpath. Selon la bibliothèque, vous pourrez peut-être utiliser le préfixe "par défaut" ou vous devrez peut-être lui attribuer un préfixe nommé et l'utiliser dans vos requêtes xpath.

Par exemple, en php (puisque vous n'avez pas spécifié de langue) en utilisant DOMXPath, vous pouvez faire quelque chose comme ceci:

$xpath = new DOMXPath($document);
$xpath->registerNamespace('x', 'http://schemas.Microsoft.com/sharepoint/soap/');
$xpath->query('/x:List/x:Fields/x:Field');
16
Anomie

Je viens d'avoir ce problème lors de l'utilisation de Xalan-c

Le problème que je n’ai pas bien compris au départ est que les alias/préfixes d’espace de nom XPath ou XSLT peuvent être différents de ceux du document, en fonction du résolveur de votre espace de nom.

Il semble que s'il existe un espace de noms sur le document, il ne correspond pas à un élément de chemin d'accès, sauf si un espace de noms est utilisé. (standard mais pas toujours suivi?)

Le XalanDocumentPrefixResolver mappera les espaces de noms XPath ou XSLT sur l'URI et essaiera de leur donner l'identifiant en obtenant le préfixe - là où il n'y a pas de préfixe, il utilisa le nom qui se transforma en xmlns.

/xmlns:List/xmlns:Fields/xmlns:Field

Sinon, vous pouvez créer votre propre résolveur, mais cela nécessite tout de même un espace de noms minimal utilisé dans le xpath :(

En voici un que j'ai piraté ensemble lors des tests, aucune garantie de mémoire

// don't care what prefix given, there can only be the one
struct NoPrefixResolver : public xalanc::PrefixResolver {

    NoPrefixResolver(const xalanc::XalanDOMString&   theURI) : m_uri(theURI){}

    virtual const xalanc::XalanDOMString*
        getNamespaceForPrefix(const xalanc::XalanDOMString&     prefix) const {
        return &m_uri;
    }

    virtual const xalanc::XalanDOMString&   getURI() const {
        return m_uri;
    }

    const xalanc::XalanDOMString    m_uri;
};

/x:List/x:Fields/x:Field 
/a:List/b:Fields/c:Field 
1
Greg Domjan