문제

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>

...그리고 우리는 값을 바꾸더라도 이 두 가지 속성을 유지하고 싶었습니다.정규 표현식을 사용하는 방법도 있나요?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top