Pregunta

¿Cómo puedo usar groovy para buscar+reemplazar en XML?

Necesito algo tan corto/fácil como sea posible, ya que voy a estar dando este código a los evaluadores para su SoapUI de secuencias de comandos.

Más específicamente, ¿cómo puedo activar:

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

en:

<root><data>value</data></root>
¿Fue útil?

Solución

Algunas de las cosas que puedes hacer con una XSLT también se puede hacer con alguna forma de 'buscar y reemplazar'.Todo depende de la complejidad de su problema y de cómo 'genérico' desea implementar la solución.Para hacer su propio ejemplo ligeramente más genérico:

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

La solución que elija depende de usted.En mi propia experiencia (por muy simples problemas) usando una simple cadena de búsquedas es más rápido que el uso de expresiones regulares, que es de nuevo más rápido que con un global de transformación XSLT (tiene sentido, en realidad).

Otros consejos

Después de algunos frenético de la codificación he visto la luz y lo hice así

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()

Por desgracia esto no va a preservar los comentarios y metadatos, etc, desde el documento xml original, así que voy a tener que encontrar otra manera

Hice algunas pruebas con DOMCategory y es casi trabajo.Puedo hacer que la reemplace suceder, pero algunos de infopath los comentarios relacionados con desaparecer.Estoy utilizando un método como este:

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

en una fuente como esta:

<?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 única cosa que faltaba en el resultado son los <?mso- lines from the result.Cualquier persona con una idea para que?

Esa es la mejor respuesta hasta ahora y da el resultado correcto, así que voy a aceptar la respuesta :) Sin embargo, es un poco demasiado grande para mí.Creo que he tenido explicar mejor que la alternativa es:

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

Pero eso no es muy xml y así que pensé en buscar una alternativa.También, no puedo estar seguro de que la primera etiqueta vacía todo el tiempo.

Para conservar los atributos de sólo modificar su pequeño programa como este (he incluido un ejemplo de fuente de prueba):

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

Ejecuta este script genera:

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

Tenga en cuenta que la coincidencia de regexp ahora contiene 3 grupos de captura:(1) la etiqueta de inicio (incluyendo los atributos), (2) lo que es el 'viejo' contenido de su etiqueta y (3) el final de la etiqueta.La cadena de reemplazo se refiere a estos capturado grupos a través de la $i (sintaxis con barras diagonales inversas para escapar de ellos, en el GString).Sólo un consejo:las expresiones regulares son muy poderosos animales, es realmente worthwile para familiarizarse con ellas ;-) .

Tres "oficial" groovy formas de actualización XML se describen en la página http://groovy.codehaus.org/Processing+XML, la sección "Actualización de XML".

De los tres parece que sólo DOMCategory la forma de preservar los comentarios XML, etc.

Para mí el real copiar y buscar & reemplazar parece el trabajo perfecto para una hoja de estilos XSLT.En un XSLT usted no tiene ningún problema en que todos simplemente copiar todo (incluyendo los artículos que usted está teniendo problemas con el) y simplemente introduzca sus datos cuando es necesario.Usted puede pasar el valor específico de sus datos a través de un parámetro XSL o dinámicamente puede modificar la hoja de estilos en sí (si se incluye como una cadena en su Maravilloso programa).Llamando a este XSLT para transformar su documento(s) desde dentro de Groovy es muy simple.

Rápidamente me empedrado el siguiente script Groovy juntos (pero no tengo dudas de que puede ser escrito aún más 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))

Ejecuta este script genera:

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

Genial!Muchas gracias por la ayuda :)

Que solucionó mi problema de una forma mucho más limpia y más fácil manera.Es un final parecido a esto:

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

Ya me voy a dar esto a nuestros testers para su uso en sus pruebas de la herramienta SoapUI, he tratado de "wrap", para hacer más fácil para ellos para copiar y pegar.

Esto es suficiente para mi propósito, pero sería perfecto si podríamos añadir uno más "twist"

Digamos que la entrada tenía esto en él...

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

...y quisimos mantener esos dos atributos, incluso a pesar de que hemos sustituido el valor.Es allí una manera de utilizar regexp para eso también?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top