Как мне использовать groovy для поиска + замены в XML? - PullRequest
2 голосов
/ 18 сентября 2008

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

Мне нужно что-то короткое / простое, насколько это возможно, так как я передам этот код тестерам для их сценариев SoapUI.

Более конкретно, как мне повернуть:

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

в

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

Ответы [ 9 ]

2 голосов
/ 23 сентября 2008

Некоторые вещи, которые вы можете делать с XSLT, вы также можете делать с помощью какой-либо формы поиска и замены. Все зависит от того, насколько сложна ваша проблема и насколько «универсальным» вы хотите реализовать решение. Чтобы сделать свой собственный пример немного более общим:

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

Решение, которое вы выберете, зависит от вас. По моему собственному опыту (для очень простых задач) использование простых поисков строк быстрее, чем использование регулярных выражений, что опять же быстрее, чем использование полнофункционального преобразования XSLT (на самом деле имеет смысл).

1 голос
/ 24 сентября 2008

Чтобы сохранить атрибуты, просто измените вашу маленькую программу следующим образом (я включил пример источника для тестирования):

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). Просто совет: регулярные выражения - очень мощные животные, с ними действительно стоит ознакомиться ;-).

1 голос
/ 22 сентября 2008

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

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

Но это не очень xml'ы, поэтому я подумал, что я буду искать альтернативу. Кроме того, я не могу быть уверен, что первый тег все время пуст.

1 голос
/ 19 сентября 2008

Я провел некоторое тестирование с DOMCategory, и оно почти работает. Я могу сделать замену, но некоторые комментарии, связанные с инфопатом, исчезают. Я использую такой метод:

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-строк из результата. Кто-нибудь с идеей для этого? </p>

1 голос
/ 18 сентября 2008

После некоторого бешеного кодирования я увидел свет и сделал вот так

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, поэтому мне придется найти другой способ

0 голосов
/ 23 сентября 2008

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

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

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>

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

0 голосов
/ 22 сентября 2008

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

Три "официальных" отличных способа обновления XML описаны на странице http://groovy.codehaus.org/Processing+XML, в разделе "Обновление XML".

Из этих трех, похоже, только способ DOMCategory сохраняет комментарии XML и т. Д.

0 голосов
/ 18 сентября 2008
...