Question

Comment utiliser groovy pour rechercher + remplacer en XML?

J'ai besoin de quelque chose d'aussi court / facile que possible, car je donnerai ce code aux testeurs pour leur script SoapUI.

Plus précisément, comment puis-je activer:

<root><data></data></root>

dans:

<root><data>value</data></root>
Était-ce utile?

La solution

Certaines choses que vous pouvez faire avec un XSLT peuvent également être faites avec une forme de 'search & amp; remplacer'. Tout dépend de la complexité de votre problème et du degré de «générique» avec lequel vous souhaitez implémenter la solution. Pour rendre votre propre exemple légèrement plus générique:

xml.replaceFirst("<Mobiltlf>[^<]*</Mobiltlf>", '<Mobiltlf>32165487</Mobiltlf>')

La solution que vous choisissez est à vous. Selon ma propre expérience (pour des problèmes très simples), utiliser des recherches de chaîne simples est plus rapide que d’utiliser des expressions régulières, ce qui est encore plus rapide que d’utiliser une transformation XSLT complète (est tout à fait logique).

Autres conseils

Après un codage frénétique, j’ai vu la lumière et fait comme ça

import org.custommonkey.xmlunit.Diff
import org.custommonkey.xmlunit.XMLUnit

def input = '''<root><data></data></root>'''
def expectedResult = '''<root><data>value</data></root>'''

def xml = new XmlParser().parseText(input)

def p = xml.'**'.data
p.each{it.value="value"}

def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(xml)
def result = writer.toString()

XMLUnit.setIgnoreWhitespace(true)
def xmlDiff = new Diff(result, expectedResult)
assert xmlDiff.identical()

Malheureusement, cela ne préservera pas les commentaires et les métadonnées, etc., du document XML original, je vais donc devoir trouver un autre moyen

J'ai fait quelques tests avec DOMCategory et ça marche presque. Je peux faire en sorte que le remplacement se produise, mais certains commentaires relatifs à l'infopath disparaissent. J'utilise une méthode comme celle-ci:

def rtv = { xml, tag, value ->
    def doc     = DOMBuilder.parse(new StringReader(xml))
    def root    = doc.documentElement
    use(DOMCategory) { root.'**'."$tag".each{it.value=value} }
    return DOMUtil.serialize(root)    
}

sur une source comme celle-ci:

<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://corp.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf></Mobiltlf>
  <E-mail-adresse></E-mail-adresse>
</application:FA_Ansoegning>

La seule chose qui manque au résultat, ce sont les <? mso- lines du résultat. Quelqu'un avec une idée pour ça?

C’est la meilleure réponse à ce jour et donne le bon résultat, alors je vais accepter la réponse :) Cependant, c'est un peu trop grand pour moi. Je pense que je devrais mieux expliquer que l’alternative est:

xml.replace("<Mobiltlf></Mobiltlf>", <Mobiltlf>32165487</Mobiltlf>")

Mais ce n'est pas très xml'y alors j'ai pensé que je chercherais une alternative. De plus, je ne peux pas être sûr que la première balise est vide tout le temps.

Pour conserver les attributs, modifiez simplement votre petit programme comme ceci (j'ai inclus un exemple de source pour le tester):

def input = """
<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://ementor.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf  type="national" anotherattribute="value"></Mobiltlf>
  <E-mail-adresse attr="whatever"></E-mail-adresse>
</application:FA_Ansoegning>
""".trim()

def rtv = { xmlSource, tagName, newValue ->
    regex = "(<$tagName[^>]*>)([^<]*)(</$tagName>)"
    replacement = "\$1${newValue}\$3"
    xmlSource = xmlSource.replaceAll(regex, replacement)
    return xmlSource
}

input = rtv( input, "Mobiltlf", "32165487" )
input = rtv( input, "E-mail-adresse", "bob@email.com" )
println input

L'exécution de ce script produit:

<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://ementor.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf  type="national" anotherattribute="value">32165487</Mobiltlf>
  <E-mail-adresse attr="whatever">bob@email.com</E-mail-adresse>
</application:FA_Ansoegning>

Notez que l'expression rationnelle correspondante contient désormais 3 groupes de capture: (1) la balise de début (y compris les attributs), (2) quel que soit le contenu de l'ancien contenu de votre balise et (3) la balise de fin. La chaîne de remplacement fait référence à ces groupes capturés via la syntaxe $ i (avec des barres obliques inverses pour les échapper dans GString). Un petit conseil: les expressions régulières sont des animaux très puissants, il est vraiment utile de se familiariser avec eux ;-).

Trois & "officiels &"; Les méthodes de mise à jour XML sont décrites à la page http://groovy.codehaus.org/Processing+XML, section & "Mise à jour de XML &";

Sur ces trois, seule la méthode DOMCategory préserve les commentaires XML, etc.

Pour moi, la copie actuelle & amp; recherche & amp; remplacer semble être le travail idéal pour une feuille de style XSLT. Dans un XSLT, vous n'avez aucun problème à tout copier (y compris les éléments avec lesquels vous rencontrez des problèmes) et à insérer simplement vos données là où elles sont requises. Vous pouvez transmettre la valeur spécifique de vos données via un paramètre XSL ou modifier dynamiquement la feuille de style elle-même (si vous l'incluez sous forme de chaîne dans votre programme Groovy). Appeler cette XSLT pour transformer vos documents à partir de Groovy est très simple.

J'ai rapidement bricolé le script Groovy suivant (mais je ne doute pas qu'il puisse être écrit encore plus simple / compact):

import javax.xml.transform.TransformerFactory
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource

def xml = """
<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:application="http://ementor.dk/application/2007/06/22/"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf></Mobiltlf>
  <E-mail-adresse></E-mail-adresse>
</application:FA_Ansoegning>
""".trim()

def xslt = """
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:param name="mobil" select="'***dummy***'"/>
    <xsl:param name="email" select="'***dummy***'"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Mobiltlf">
        <xsl:copy>
            <xsl:value-of select="\$mobil"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="E-mail-adresse">
        <xsl:copy>
            <xsl:value-of select="\$email"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
""".trim()

def factory = TransformerFactory.newInstance()
def transformer = factory.newTransformer(new StreamSource(new StringReader(xslt)))

transformer.setParameter('mobil', '1234567890')
transformer.setParameter('email', 'john.doe@foobar.com')

transformer.transform(new StreamSource(new StringReader(xml)), new StreamResult(System.out))

L'exécution de ce script produit:

<?xml version="1.0" encoding="UTF-8"?><?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:FA_Ansoegning:http---ementor-dk-application-2007-06-22-" href="manifest.xsf" solutionVersion="1.0.0.14" productVersion="12.0.0" PIVersion="1.0.0.0" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<application:FA_Ansoegning xmlns:application="http://ementor.dk/application/2007/06/22/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/200    8-04-14T14:31:48">
    <Mobiltlf>1234567890</Mobiltlf>
  <E-mail-adresse>john.doe@foobar.com</E-mail-adresse>
</application:FA_Ansoegning>

Brillant! Merci beaucoup pour votre aide:)

Cela résout mon problème de manière beaucoup plus propre et plus facile. Cela a fini par ressembler à ceci:

def rtv = { xmlSource, tagName, newValue ->
    regex = "<$tagName>[^<]*</$tagName>"
    replacement = "<$tagName>${newValue}</$tagName>"
    xmlSource = xmlSource.replaceAll(regex, replacement)
    return xmlSource
}

input = rtv( input, "Mobiltlf", "32165487" )
input = rtv( input, "E-mail-adresse", "bob@email.com" )
println input

Depuis que je donne ceci à nos testeurs pour qu'ils l'utilisent dans leur outil de test SoapUI, j'ai essayé de & "; boucler &"; pour le rendre plus facile à copier et coller.

C’est assez bien pour mon propos, mais ce serait parfait si nous pouvions en ajouter un & "twist &";

Disons que l'entrée avait ceci dedans ...

<Mobiltlf type="national" anotherattribute="value"></Mobiltlf>

... et nous voulions conserver ces deux attributs même si nous avons remplacé la valeur. Y at-il un moyen d’utiliser regexp pour cela aussi?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top