IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Format d'échange (XML, JSON...) Java Discussion :

Transformation XSL puis requetage XPath sur le noeud résultat


Sujet :

Format d'échange (XML, JSON...) Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre régulier
    Inscrit en
    Mars 2003
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Mars 2003
    Messages : 10
    Par défaut Transformation XSL puis requetage XPath sur le noeud résultat
    Bonjour,

    J'essaie d'appliquer une transormation XSL toute simple puis de faire une requête XPath sur le noeud résultat de la transformation. Mais je n'arrive pas à obtenir le noeud via la requete XPath. J'ai essayé plein de piste et en testant dans tous les sens, j'ai fini par enregistrer le XML dans un fichier, le recharger et appliquer ma requête XPath dessus : dans ce cas cela fonctionne mais ce n'est bien sûr pas satisfaisant.

    Voici le code de mon test (les méthodes utilitaires ont été supprimées pour plus de lisibilité). Le code complet est dans la pièce jointe sous la forme d'un projet Maven avec un test unitaire.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    	@Test
    	public void testWithNativeJavaApiAndIntermediateFile() throws Exception {
    		InputStream instream = resolveClasspathFile("xslt/xslt-test-transform-2.xsl");
    		StreamSource xsltSource = new StreamSource(instream);
    		DOMSource domSource = loadXmlFromClasspathFile("xslt/xslt-test-input-2.xml");
    		prettyPrint(domSource.getNode());
     
    		Transformer transformer = TransformerFactory.newInstance().newTransformer(xsltSource);
    		DOMResult domResult = new DOMResult();
    		transformer.transform(domSource, domResult);
    		Node node = domResult.getNode();
     
    		// Store then reload the file
    		// Comment those 3 lignes to test without intermediate file
    		File xslOutputfile = new File("target", "xsl-ouput.xml");
    		prettyPrint(node, new FileOutputStream(xslOutputfile));
    		node = loadXmlFromInputStream(new FileInputStream(xslOutputfile)).getNode();
     
    		XPath xPathProcessor = XPathFactory.newInstance().newXPath();
    		XPathExpression xpathExpression = xPathProcessor.compile("/Message/Out/Personne/CodeCivilite");
    		System.out.println();
    		Node resultNode = (Node) xpathExpression.evaluate(node, XPathConstants.NODE);
     
    		if (resultNode != null) {
    			System.out.println(resultNode.getNodeName() + "=" + resultNode.getTextContent());
    		} else {
    			System.out.println("Node is null");
    		}
     
    		assertNotNull(resultNode);
    		assertEquals("CodeCivilite", resultNode.getNodeName());
    		assertEquals("M.", resultNode.getTextContent());
    	}
    Toute aide est la bienvenue, je suis complètement bloqué !

    Merci
    Fichiers attachés Fichiers attachés

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 585
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 585
    Par défaut
    Houlàlà je vois ce qui se passe. C'est compliqué.

    C'est à cause de la gestion désastreuse des namespaces par Java.
    Par défaut, DocumentBuilderFactory considère que les namespaces, ça n'existe pas. On peut changer ce comportement en appelant setNamespaceAware(true) dessus, mais si on le fait pas, par défaut, les namespaces ça existe pas.

    Normalement, tu devrais échouer avec les deux méthodes. Mais dans le cas de passer par un fichier temporaire, la non-gestion des namespaces te sauve.

    Seulement, quand tu ne passes pas par un fichier temporaire, ce n'est pas ton DocumentBuilderFactory qui te génère le document. C'est ton TransformerFactory. Et lui il est bien obligé de gérer les namespaces, parceque XSLT c'est basé à fond sur les namespaces. Il te génère donc un document dans lequel les namespaces, ça existe, et tous les éléments sont dans le namespace http://www.example.org/test.

    Ces éléments étant dans un namespace, tu ne peux pas les atteindre avec un XPath genre /Message/Out/Personne/CodeCivilite. En effet, avec ce XPath tu n'indiques pas dans quel namespace sont les éléments. Or c'est obligatoire.
    Il faut que ton XPath ressemble à ça : /t:Message/t:Out/t:Personne/t:CodeCivilite.
    Tous les éléments doivent avoir un préfixe, là j'ai choisi t: pour test, qui indique qu'ils sont dans un namespace.
    Il reste à lier ce préfixe t: au namespace http://www.example.org/test, et ça se fait comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    xPathProcessor.setNamespaceContext(new NamespaceContext() {
      @Override
      public String getNamespaceURI(String prefix) {
        if(prefix.equals("t")) {
          return "http://www.example.org/test";
        } else {
          return null;	
        }
      }
      @Override public String getPrefix(String namespaceURI) {
        return null; // on s'en fout
      }
      @Override
      public Iterator<?> getPrefixes(String namespaceURI) {
        return null; // on s'en fout aussi
      }
    });
    Avec ces deux corrections, tu peux essayer, la méthode sans fichier temporaire marche.

    Mais la méthode avec fichier temporaire ne marche plus -_-°. Parce qu'on vient de gérer les namespaces, alors que la méthode avec fichier temporaire n'a pas de namespaces.
    Pour corriger ça, il faudrait faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    dbf.setNamespaceAware(true);
    Mais si on fait ça, les deux méthodes cessent de marcher ! Parce que la transformation XSLT ne tranforme plus les <In> en <Out> !

    Eh oui. Parce que Java de base ne gère pas XSLT 2.0 mais XSLT 1.0. En XSLT 1.0, xpath-default-namespace n'existe pas. Par conséquent, si tu fais un match comme
    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    <xsl:template match="In">
    Il considère que c'est les éléments <In> sans namespace, pas ceux avec namespace. Avant ça marchait, parce que les namespaces n'existaient pas.
    Mais maintenant qu'on les a activés, ça ne marche plus !

    Deux possibilités :
    - utiliser un moteur XSLT compatible 2.0, comme Saxon par exemple.
    - modifier la XSLT, avec un match comme ça :
    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    <xsl:template match="t:In">
    et ajouter xmlns:t="http://www.example.org/test" dans la stylesheet.

    => Problèmes résolus !

    Ça aurait été plus simple avec JDOM :

    - Lui il gère toujours les namespaces. Tu aurais eu les mêmes problèmes avec les deux méthodes.
    - Faire une requête XPath avec namespace est plus simple.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre régulier
    Inscrit en
    Mars 2003
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Mars 2003
    Messages : 10
    Par défaut
    Ok merci c'est clair et limpide !
    N'y a-t-il pas moyen de déclarer un namespace par défaut (sans alias/prefix) pour les requêtes XPath, histoire d'améliorer la lisibilité ?

  4. #4
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 585
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 585
    Par défaut
    Pas en XPath 1.0, et à ma connaissance Java ne fournit que du XPath 1.0.

    Ce serait faisable avec Saxon et sa gestion du XPath 2.0. (Mais pour être honnête, je ne sais pas comment.)
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre régulier
    Inscrit en
    Mars 2003
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Mars 2003
    Messages : 10
    Par défaut
    Pas grave, je vais me contenter de ceci et maintenant avec une meilleure gestion des namespace, cela fonctionne bien en effet.
    Merci beaucoup !

Discussions similaires

  1. XPATH > une boucle sur des noeuds enfants ?
    Par mastronic dans le forum Débuter
    Réponses: 6
    Dernier message: 09/10/2009, 17h03
  2. [xpath] startsWith sur un noeud
    Par michael08 dans le forum XSL/XSLT/XPATH
    Réponses: 5
    Dernier message: 05/01/2009, 18h46
  3. [XPath/VB.Net] Comment pointer sur un noeud enfant ?
    Par Petit_ange dans le forum Accès aux données
    Réponses: 5
    Dernier message: 27/08/2006, 14h15
  4. [XSL]Test sur le noeud fils suivant?
    Par kobe dans le forum XSL/XSLT/XPATH
    Réponses: 9
    Dernier message: 06/07/2006, 15h25
  5. Test sur le noeud frère suivant (XSLT, XPath)
    Par rprom1 dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 23/02/2006, 17h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo