Как мне использовать groovy для поиска + замены в XML?

StackOverflow https://stackoverflow.com/questions/91957

  •  01-07-2019
  •  | 
  •  

Вопрос

Как мне использовать 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- lines from the result.У кого-нибудь есть идея по этому поводу?

Это лучший ответ на данный момент, и он дает правильный результат, поэтому я собираюсь принять ответ :) Однако для меня он немного великоват.Я думаю, мне лучше объяснить, что альтернативой является:

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 way сохраняет XML-комментарии и т.д.

Мне фактическое копирование, поиск и замена кажутся идеальной работой для таблицы стилей 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>

Блестяще!Большое вам спасибо за вашу помощь :)

Это решает мою проблему гораздо чище и проще.В итоге все выглядит вот так:

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>

... и мы хотели сохранить эти два атрибута, даже несмотря на то, что заменили значение.Есть ли способ использовать regexp и для этого?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top