Rudziankoŭ
Rudziankoŭ

Reputation: 11251

Format string in groovy

I would like to substitute %s with the value

<server>
    <id>artifactory</id>
    <username>%s</username>
    <password>%s</password>
</server>

Is there any myString.format("name", "pass") method in groovy?

Upvotes: 23

Views: 73000

Answers (5)

marverix
marverix

Reputation: 7705

Groovy has a SimpleTemplateEngine that you can use. This may be an overkill for simple cases, but for example let's say that you want to read a template from a file, and inject variables there. Using %s would be a nightmare. SimpleTemplateEngine is a way to go here:

import groovy.text.SimpleTemplateEngine

def engine = new SimpleTemplateEngine()
def template = 'User: ${user}\nPassword: ${password}'

def binding = [
  "user": "root",
  "password": "toor"
]

def result = engine.createTemplate(template).make(binding).toString()

assert result == "User: root\nPassword: toor"

So to summarize:

  1. Groovy String Interpolation

    def user = "root"
    def password = "toor"
    // Note: Remember to use single quotes!
    def result = "User: ${user}\nPassword: ${password}"
    
    assert result == "User: root\nPassword: toor"
    

    Pros:

    • Quick
    • Simple
    • Generally good readability
    • You can do almost everything in the mustaches

    Cons:

    • Variables must be accessible at the moment of the interpolation
    • Readability can drop drastically developer gives in to the temptation to put too many things between the mustaches, e.g. "Hello ${someRandomFunction('Arnold', anotherFunction(arr[1]))}"
  2. Java String.format

    // Now template can be defined before variables
    def template = "User: %s\nPassword: %s"
    def user = "root"
    def password = "toor"
    def result = String.format(template, user, password)
    
    assert result == "User: root\nPassword: toor"
    

    Pros:

    • Quick
    • Simple
    • Standard Java library, so Java developers will be familiar with it
    • Good readability for a small number of variables
    • Built-in numbers-formatter (like adding leading zeros, following zeros, etc.)
    • You can define template before variables

    Cons:

    • Readability drops drastically for a larger number of variables because you end with templates like Hello %s, I'm %s and I'm %d old. I live in %s, %s. My %s is %s.
    • Variables must be passed as separate arguments
  3. Groovy sprintf

    def template = "User: %s\nPassword: %s"
    def result = sprintf(template, ["root", "toor"])
    
    assert result == "User: root\nPassword: toor"
    

    Pros:

    • The same as for Java String.format
    • You can pass variables as a single array

    Cons:

    • The same as for Java String.format expect cons with passing variables
  4. Groovy SimpleTemplateEngine

    import groovy.text.SimpleTemplateEngine
    
    def engine = new SimpleTemplateEngine()
    def template = 'User: ${user}\nPassword: ${password}'
    
    def binding = [
      "user": "root",
      "password": "toor"
    ]
    
    def result = engine.createTemplate(template).make(binding).toString()
    
    assert result == "User: root\nPassword: toor"
    

    Pros:

    • Generally good readability
    • You can do almost everything in the mustaches
    • You can define/read template before variables, which is perfect for larger templates kept in a text files

    Cons:

    • You need to do the import and create an instance, so for sure it's not as quick and simple solution as above
    • Readability can drop drastically developer gives in to the temptation to put too many things between the mustaches, e.g. "Hello ${someRandomFunction('Arnold', anotherFunction(arr[1]))}"

Generally, my favorite is Groovy's String interpolation and the SimpleTemplateEngine, because I don't mind importing one class more. But choice is yours :)

Upvotes: 0

Dmitrii Semikin
Dmitrii Semikin

Reputation: 2584

Collecting the answers and comments above in the single place, there are two approaches:

  1. Based on Java String.format functionality see above, which is in groovy wrapped into convenience function sprintf see e.g. here or here
  2. Based on string interpolation and more specifically this case.
  3. [edit] As suggested by ernest_k in comments, the better solution for templating is template engines. It is way more powerful, then bare String.format, but of course, requires some learning.

In my personal opinion, first option is better fit for the originally asked question, because it allows for creating "template" strings, which can be then used with arbitrary parameters in arbitrary place in the code arbitrary number of times. This seems to be the use-case in the original question.

Generally there are two major differences between string interpolation and sprintf:

  1. String interpolation does not allow to format the values.
  2. String interpolation is evaluated in place, where it is defined (in its normal form), so it does not allow templating. Even if used in its "special" form ({-> expr}), it still does not have explicit act of template application, but rather rely on externally defined variables with particular names, which is in my opinion less reliable.

As an illustration for the templating usecase, consider:

// can be defined in the same function, or as static variable in the class or even
// in other class
final String URL_TEMPLATE = 'http://fake.weather.com/rest/%s' // arg - country abbreviation

// ...

// Here we want to get weather for different countries.
def countries = ['US', 'DE', 'GB']
for (country in countries) {
    def url = URL_TEMPLATE.format(country)
    // send the request, accumulate the results
}

This solution is not easy to use using string interpolation. Of course, I admit, that it is still possible to build the solution also based on string interpolation, but it would look much different.

Upvotes: 3

Mike W
Mike W

Reputation: 3932

You can use sprintf from DefaultGroovyMethods

def name = "name"
def pass = "pass"

String formatted = """
<server>
    <id>artifactory</id>
    <username>$name</username>
    <password>$pass</password>
</server>
"""
def f = sprintf( formatted, name, pass )

Upvotes: 5

ernest_k
ernest_k

Reputation: 45339

Groovy has built-in support for string interpolation. All you need is to use a GString:

def name = "name"
def pass = "pass"

String formatted = """
<server>
    <id>artifactory</id>
    <username>$name</username>
    <password>$pass</password>
</server>
"""

If your values come as an array or collection, you can even use params[n] instead of named variables ($name), like this:

def params = ['name', 'pass']

String formatted = """
<server>
    <id>artifactory</id>
    <username>${params[0]}</username>
    <password>${params[1]}</password>
</server>
"""

If your string needs to be externalized, you can use template engines

Beside this, you can use the normal Java String.format:

def formatted = String.format(myString, "name", "pass")

Upvotes: 27

daggett
daggett

Reputation: 28634

groovy based on java and in java there is a format method in String class

https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#format(java.lang.String,%20java.lang.Object...)

so this should work

def s='''<server>
    <id>artifactory</id>
    <username>%s</username>
    <password>%s</password>
</server>'''
println String.format(s, "name", "pass")

Upvotes: 18

Related Questions