如何使用groovy在XML中搜索+替换?

时间:2008-09-18 12:12:56

标签: xml groovy soapui

如何使用groovy在XML中搜索+替换?

我需要尽可能短/容易的东西,因为我将把这些代码提供给测试人员用于他们的SoapUI脚本。

更具体地说,我该怎么转:

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

成:

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

9 个答案:

答案 0 :(得分:2)

你可以用XSLT做的一些事情也可以用某种形式的'搜索&amp;更换'。这完全取决于您的问题的复杂程度以及您希望如何实现解决方案的“通用”。为了让你自己的例子更加通用:

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

您选择的解决方案取决于您。根据我自己的经验(对于非常简单的问题),使用简单的字符串查找比使用正则表达式更快,这比使用完整的XSLT转换更快(实际上有意义)。

答案 1 :(得分:1)

经过一些疯狂的编码,我看到了光,并且做了这样的

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

不幸的是,这不会保留原始xml文档中的注释和元数据等,所以我必须找到另一种方式

答案 2 :(得分:1)

我使用DOMCategory做了一些测试,它几乎正常工作。我可以让替换发生,但一些与infopath相关的评论消失了。我正在使用这样的方法:

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

在这样的来源上:

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

结果中唯一缺少的是结果中的&lt;?mso-行。有想法的人吗?

答案 3 :(得分:1)

到目前为止,这是最好的答案,它给出了正确的结果,所以我将接受答案:) 但是,它对我来说有点太大了。我想我最好解释一下,替代方案是:

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

但那不是非常xml'y,所以我想我会寻找另一种选择。另外,我无法确定第一个标签是否一直是空的。

答案 4 :(得分:1)

要保留属性,只需像这样修改你的小程序(我已经包含了一个示例源来测试它):

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

运行此脚本会产生:

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

请注意,匹配的正则表达式现在包含3个捕获组:(1)开始标记(包括属性),(2)标记的“旧”内容和(3)结束标记。替换字符串通过$ i语法引用这些捕获的组(使用反斜杠在GString中转义它们)。只是一个提示:正则表达式是非常强大的动物,熟悉它们真的很值得;-)。

答案 5 :(得分:0)

答案 6 :(得分:0)

http://groovy.codehaus.org/Processing+XML页“更新XML”一节中介绍了三种“官方”常规更新XML的方法。

在这三个中,似乎只有DOMCategory方式保留XML注释等。

答案 7 :(得分:0)

对我来说,实际的副本和搜索&amp;替换似乎是XSLT样式表的完美工作。在XSLT中,您完全没有问题只需复制所有内容(包括您遇到问题的项目),只需将数据插入到需要的地方即可。您可以通过XSL参数传递数据的特定值,也可以动态修改样式表本身(如果在Groovy程序中包含为字符串)。调用此XSLT从Groovy中转换文档非常简单。

我快速拼凑了以下Groovy脚本(但我毫不怀疑它可以写得更简单/紧凑):

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

运行此脚本会产生:

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

答案 8 :(得分:0)

辉煌!非常感谢你的帮助:))

以更清洁,更简单的方式解决了我的问题。结果看起来像这样:

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

由于我将这个用于我们的测试人员用于他们的测试工具SoapUI,我试图“包装”它,以便他们更容易复制和粘贴。

这对我的目的来说已经足够了,但如果我们再添加一个“扭曲”

,这将是完美的

让我们说输入中有这个......

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

...我们想要保留两个属性,即使我们替换了这个值。有没有办法使用正则表达式呢?