Groovy를 사용하여 XML에서 검색+바꾸기를 어떻게 하나요?
문제
Groovy를 사용하여 XML에서 검색+바꾸기를 어떻게 하나요?
SoapUI 스크립팅을 위해 테스터에게 이 코드를 제공할 것이기 때문에 가능한 한 짧고 쉬운 것이 필요합니다.
보다 구체적으로 어떻게 설정합니까?
<root><data></data></root>
안으로:
<root><data>value</data></root>
해결책
XSLT로 수행할 수 있는 작업 중 일부는 '검색 및 바꾸기' 형식으로도 수행할 수 있습니다.그것은 모두 문제가 얼마나 복잡한지, 그리고 솔루션을 얼마나 '일반적'으로 구현하려는지에 달려 있습니다.자신의 예를 좀 더 일반적인 것으로 만들려면 다음을 수행하십시오.
xml.replaceFirst("<Mobiltlf>[^<]*</Mobiltlf>", '<Mobiltlf>32165487</Mobiltlf>')
당신이 선택하는 솔루션은 당신에게 달려 있습니다.내 경험에 따르면(아주 간단한 문제의 경우) 간단한 문자열 조회를 사용하는 것이 정규식을 사용하는 것보다 빠르며 이는 본격적인 XSLT 변환을 사용하는 것보다 더 빠릅니다(실제로는 의미가 있음).
다른 팁
열광적인 코딩을 한 후 나는 빛을 보고 이렇게 했습니다.
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 문서의 주석과 메타데이터 등이 보존되지 않으므로 다른 방법을 찾아야 합니다.
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>
결과에서 누락된 유일한 것은 결과의 <?mso- 라인입니다.그것에 대한 아이디어가 있는 사람이 있나요?
그것은 지금까지 최고의 대답이며 올바른 결과를 제공하므로 답을 받아 들일 것입니다.내 생각에는 대안이 다음과 같다고 설명하는 것이 더 좋을 것 같습니다.
xml.replace("<Mobiltlf></Mobiltlf>", <Mobiltlf>32165487</Mobiltlf>")
하지만 그것은 XML 형식이 아니기 때문에 대안을 찾아야겠다고 생각했습니다.또한 첫 번째 태그가 항상 비어 있는지 확신할 수 없습니다.
속성을 유지하려면 다음과 같이 작은 프로그램을 수정하세요(테스트할 샘플 소스를 포함했습니다).
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에서 이스케이프하기 위한 백슬래시 포함)을 통해 이러한 캡처된 그룹을 참조합니다.팁:정규식은 매우 강력한 동물이므로 익숙해지는 것이 정말 가치가 있습니다 ;-) .
XML을 업데이트하는 세 가지 "공식적인" 그루비 방법이 페이지에 설명되어 있습니다. http://groovy.codehaus.org/Processing+XML, "XML 업데이트" 섹션.
그 세 가지 중 DOMCategory 방식만이 XML 주석 등을 보존하는 것 같습니다.
나에게 실제 복사, 검색 및 교체는 XSLT 스타일시트에 대한 완벽한 작업인 것 같습니다.XSLT에서는 모든 것(문제가 있는 항목 포함)을 복사하고 필요한 곳에 데이터를 삽입하는 데 전혀 문제가 없습니다.XSL 매개변수를 통해 데이터의 특정 값을 전달하거나 스타일시트 자체를 동적으로 수정할 수 있습니다(Groovy 프로그램에 문자열로 포함하는 경우).Groovy 내에서 문서를 변환하기 위해 이 XSLT를 호출하는 것은 매우 간단합니다.
나는 신속하게 다음 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>
멋진!도움주셔서 정말 감사드립니다 :)
그러면 훨씬 더 깨끗하고 쉬운 방법으로 내 문제가 해결됩니다.결과는 다음과 같습니다.
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>
...그리고 우리는 값을 바꾸더라도 이 두 가지 속성을 유지하고 싶었습니다.정규 표현식을 사용하는 방법도 있나요?