Pergunta

Como posso usar Groovy para procurar + substituir em XML?

Eu preciso de algo tão curto / mais fácil possível, desde que eu vou estar dando este código para os testadores para a sua scripting SoapUI.

Mais especificamente, como faço para virar:

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

em:

<root><data>value</data></root>
Foi útil?

Solução

Algumas das coisas que você pode fazer com um XSLT você também pode fazer com alguma forma de 'procurar e substituir'. Tudo depende de quão complexo é o seu problema e como 'genérico' você deseja implementar a solução. Para fazer o seu próprio exemplo um pouco mais genérico:

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

A solução que você escolhe é até você. Na minha própria experiência (para problemas muito simples), utilizando pesquisas simples string é mais rápido do que usando expressões regulares que é novamente mais rápido do que usando uma transformação XSLT fullblown (faz sentido, na verdade).

Outras dicas

Depois de algum frenética codificação i viu a luz e fez como este

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

Infelizmente isso não vai preservar os comentários e metadados etc, a partir do documento XML original, então eu vou ter que encontrar outra maneira

Eu fiz alguma alguns testes com DOMCategory e está quase a trabalhar. Eu posso fazer a substituir acontecer, mas alguns comentários relacionados InfoPath desaparecer. Eu estou usando um 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)    
}

em uma fonte 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>

A única coisa que falta a partir do resultado são os

Essa é a melhor resposta até agora e dá o resultado certo, então eu vou aceitar a resposta :) No entanto, é um pouco grande demais para mim. Eu acho que tinha explicar melhor que a alternativa é:

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

Mas isso não é muito xml'y então eu pensei que eu iria procurar uma alternativa. Além disso, eu não posso ter certeza de que a primeira tag é esvaziar o tempo todo.

Para manter os atributos basta modificar o seu pequeno programa como este (eu incluí uma fonte de exemplo para testá-lo):

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

A execução deste script produz:

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

Note que a expressão regular de correspondência agora contém 3 grupos de captura: (1) a marca de início (incluindo atributos), (2) qualquer que seja o conteúdo 'velho' de sua tag e (3) a marca de fim. O texto de substituição refere-se a esses grupos capturados via a sintaxe $ i (com barras invertidas para escapar-los no gstring). Apenas uma dica:. Expressões regulares são animais muito poderosos, é realmente worthwile para se familiarizar com eles ;-)

Três formas "oficiais" groovy de XML atualização estão descritos na página http: //groovy.codehaus. org / Processamento + XML , seção "Atualizando XML".

Do que três só parece DOMCategory caminho preserva a comentários XML etc.

Para mim a cópia real e pesquisar e substituir Parece o trabalho perfeito para uma folha de estilo XSLT. Em um XSLT você não tem nenhum problema em tudo apenas copiar tudo (incluindo os itens que você está tendo problemas com) e simplesmente inserir seus dados onde é necessário. Você pode passar o valor específico de seus dados em via um parâmetro XSL ou você pode modificar dinamicamente o próprio estilo (se você incluir como uma seqüência em seu programa Groovy). Chamar esse XSLT para transformar o seu documento (s) a partir de dentro Groovy é muito simples.

Eu rapidamente paralelepípedos o seguinte script Groovy juntos (mas não tenho dúvidas de que podem ser escritos ainda mais simples / compactos):

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

A execução deste script produz:

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

Brilliant! Muito obrigado para você assistência:)

Isso resolve o meu problema em um muito mais limpo e mais fácil maneira. É acabou parecendo assim:

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

Desde que eu estou dando a este a nossos testadores para uso em sua ferramenta de teste SoapUI, eu tentei "envoltório" que, para tornar mais fácil para eles para copiar e colar.

Esta é uma boa o suficiente para o meu propósito, mas seria perfeito se pudéssemos adicionar mais um "twist"

Vamos dizer a entrada tinha essa nela ...

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

... e queríamos manter thos dois atributos mesmo que substituíram o valor. Existe uma maneira de usar regexp para isso também?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top