Come faccio a usare groovy di ricerca+sostituire in XML?
Domanda
Come faccio a usare groovy di ricerca+sostituire in XML?
Ho bisogno di qualcosa di più breve e semplice possibile, in quanto sarò dare questo codice per i tester per il loro SoapUI di scripting.
Più specificamente, come faccio a girare:
<root><data></data></root>
in:
<root><data>value</data></root>
Soluzione
Alcune delle cose che si possono fare con un XSLT si può anche fare con una qualche forma di "search & replace".Dipende da quanto è complesso il problema è come e 'generico' che si desidera implementare la soluzione.Per rendere il tuo esempio leggermente più generico:
xml.replaceFirst("<Mobiltlf>[^<]*</Mobiltlf>", '<Mobiltlf>32165487</Mobiltlf>')
La soluzione scelta è fino a voi.Nella mia esperienza (molto semplici problemi utilizzando la semplice stringa di ricerche è più veloce rispetto all'utilizzo di espressioni regolari, che è ancora più veloce rispetto all'utilizzo di una completa trasformazione XSLT (senso in realtà).
Altri suggerimenti
Dopo alcuni frenetica di codifica ho visto la luce e fatto come questo
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()
Purtroppo questo non conservare i commenti e i metadati ecc, dal documento xml originale, quindi dovrò trovare un altro modo
Ho fatto alcuni test con DOMCategory e di lavoro.Posso fare la sostituzione avvenga, ma alcuni di infopath relativi commenti scompaiono.Io sto usando un metodo simile a questo:
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)
}
su una fonte di simile a questo:
<?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>
L'unica cosa che mancava il risultato e ' il <?mso- lines from the result.Chiunque abbia un idea di che?
Che è la risposta migliore finora e si dà il giusto risultato, così ho intenzione di accettare la risposta :) Tuttavia, è un po ' troppo grande per me.Penso che farei meglio a spiegare che l'alternativa è:
xml.replace("<Mobiltlf></Mobiltlf>", <Mobiltlf>32165487</Mobiltlf>")
Ma non è molto xml y così ho pensato di cercare un'alternativa.Inoltre, non posso essere sicuro che il primo tag è vuoto per tutto il tempo.
Per mantenere gli attributi basta modificare il tuo piccolo programma come questo (ho incluso un esempio di fonte di prova):
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'esecuzione di questo script produce:
<?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>
Nota che corrispondono all'espressione regolare ora contiene 3 gruppi di cattura:(1) il tag di inizio (inclusi gli attributi), (2), qualsiasi sia il 'vecchio' contenuto del tag e (3) il tag di chiusura.La stringa di sostituzione si riferisce a questi gruppi catturati attraverso il $i di sintassi (con le barre rovesciate, per poi fuggire in GString).Solo un consiglio:le espressioni regolari sono uno strumento molto potente animali, è davvero ottima per acquisire familiarità con loro ;-) .
controllare questo:http://today.java.net/pub/a/today/2004/08/12/groovyxml.html?page=2
Tre "ufficiale" groovy percorsi di aggiornamento XML sono descritte in questa pagina http://groovy.codehaus.org/Processing+XML, la sezione "Aggiornamento XML".
Dei tre sembra solo DOMCategory modo conserva i commenti XML etc.
A me la copia reale & cerca & sostituisci sembra che il lavoro perfetto per un foglio di stile XSLT.In un XSLT è avere alcun problema a tutti di copiare tutto (compresi gli elementi che si stanno avendo problemi con l') e semplicemente inserisci i tuoi dati dove è richiesto.È possibile passare il valore specifico dei dati tramite un parametro XSL o è possibile modificare dinamicamente il foglio di stile (se si includono come una stringa di Groovy programma).Chiamando questo XSLT per trasformare il documento(s) da Groovy è molto semplice.
Ho subito acciottolate il seguente script Groovy insieme (ma non ho dubbi che può essere scritto anche più semplice/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'esecuzione di questo script produce:
<?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>
Geniale!La ringrazio molto per l'aiuto :)
Che risolve il mio problema in modo molto più pulito e più facile.È finito così:
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
Dal momento che sto dando questo per i nostri tester per l'uso nel loro strumento di test SoapUI, ho cercato di "avvolgere", per rendere più facile per loro di copia e incolla.
Questo è abbastanza buono per il mio scopo, ma sarebbe perfetto se si potesse aggiungere un "twist"
Diciamo che l'ingresso era questo...
<Mobiltlf type="national" anotherattribute="value"></Mobiltlf>
...e abbiamo voluto mantenere quei due attributi, anche se abbiamo sostituito il valore.C'è un modo per usare le regexp per questo?