smeeb smeeb - 8 months ago 84
Groovy Question

Groovy string replaceAll not working as expected

I have a string of valid XML (containing XML tags, attributes, comments, whitespaces, etc.) that contains special tokens of the form:


For example,
, or
, etc.

I am trying to replace all instances of
. So for example, if
exists within my XML string (regardless of how many occurrences), then I want it converted into
, etc.

My best attempt thus far:

static void main(String[] args) {
// In reality the XML string will be generated dynamically at runtime.
String xml = """
<project name="some-name" default="dist" basedir="." xmlns:ivy="antlib:org.apache.ivy.ant">

<!-- specify properties in -->
<property file="" />

<taskdef name="ivy-configure" classname="org.apache.ivy.ant.IvyConfigure" />
<taskdef name="ivy-resolve" classname="org.apache.ivy.ant.IvyResolve" />
<taskdef name="ivy-retrieve" classname="org.apache.ivy.ant.IvyRetrieve" />
<taskdef name="ivy-cleancache" classname="org.apache.ivy.ant.IvyCleanCache" />

<!-- Identify the Ivy settings file to use -->
<ivy:settings file="%(ivySettingsFile)" />

<target name="test-resolve">
<fileset dir="%(testLibDir)" includes="*.jar" />
<ivy:resolve file="test-ivy.xml" conf="compile" />
<ivy:retrieve pattern="%(testLibDir)/[artifact]-[type]-[revision].[ext]" conf="compile" />
println "Final XML is:\n${xml.replaceAll('%(', '\${').replaceAll(')', '}')}"

above produces the following stacktrace:

log4j:ERROR Could not find value for key log4j.appender.stdout.layout
log4j:WARN No such property [file] in org.apache.log4j.ConsoleAppender.
Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed group near index 2
at java.util.regex.Pattern.error(
at java.util.regex.Pattern.accept(
at java.util.regex.Pattern.group0(
(rest of stack trace omitted for brevity)

Any ideas where I'm going awry and what I can do to fix my string replacement?


You need to use a regex like:


to match

  • % - a percentage sing
  • \( - a literal (
  • ([^()]*) - Group 1 capturing 0+ chars other than ( and )
  • \) - a literal )

Note that the pattern itself is better defined with a slashy strings, and the literal $ in the replacement pattern should be escaped with a literal \ to be treated as a literal dollar symbol. $1 is a backreference to the value captured by Group 1.

Groovy demo:

println xml.replaceAll(/%\(([^()]*)\)/, '\\${$1}')