String replacement in java, similar to a velocity template
Solution 1
Use StringSubstitutor
from Apache Commons Text.
https://commons.apache.org/proper/commons-text/
It will do it for you (and its open source...)
Map<String, String> valuesMap = new HashMap<String, String>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target}.";
StringSubstitutor sub = new StringSubstitutor(valuesMap);
String resolvedString = sub.replace(templateString);
Solution 2
Take a look at the java.text.MessageFormat
class, MessageFormat takes a set of objects, formats them, then inserts the formatted strings into the pattern at the appropriate places.
Object[] params = new Object[]{"hello", "!"};
String msg = MessageFormat.format("{0} world {1}", params);
Solution 3
My preferred way is String.format()
because its a oneliner and doesn't require third party libraries:
String message = String.format("Hello! My name is %s, I'm %s.", name, age);
I use this regularly, e.g. in exception messages like:
throw new Exception(String.format("Unable to login with email: %s", email));
Hint: You can put in as many variables as you like because format()
uses Varargs
Solution 4
I threw together a small test implementation of this. The basic idea is to call format
and pass in the format string, and a map of objects, and the names that they have locally.
The output of the following is:
My dog is named fido, and Jane Doe owns him.
public class StringFormatter {
private static final String fieldStart = "\\$\\{";
private static final String fieldEnd = "\\}";
private static final String regex = fieldStart + "([^}]+)" + fieldEnd;
private static final Pattern pattern = Pattern.compile(regex);
public static String format(String format, Map<String, Object> objects) {
Matcher m = pattern.matcher(format);
String result = format;
while (m.find()) {
String[] found = m.group(1).split("\\.");
Object o = objects.get(found[0]);
Field f = o.getClass().getField(found[1]);
String newVal = f.get(o).toString();
result = result.replaceFirst(regex, newVal);
}
return result;
}
static class Dog {
public String name;
public String owner;
public String gender;
}
public static void main(String[] args) {
Dog d = new Dog();
d.name = "fido";
d.owner = "Jane Doe";
d.gender = "him";
Map<String, Object> map = new HashMap<String, Object>();
map.put("d", d);
System.out.println(
StringFormatter.format(
"My dog is named ${d.name}, and ${d.owner} owns ${d.gender}.",
map));
}
}
Note: This doesn't compile due to unhandled exceptions. But it makes the code much easier to read.
Also, I don't like that you have to construct the map yourself in the code, but I don't know how to get the names of the local variables programatically. The best way to do it, is to remember to put the object in the map as soon as you create it.
The following example produces the results that you want from your example:
public static void main(String[] args) {
Map<String, Object> map = new HashMap<String, Object>();
Site site = new Site();
map.put("site", site);
site.name = "StackOverflow.com";
User user = new User();
map.put("user", user);
user.name = "jjnguy";
System.out.println(
format("Hello ${user.name},\n\tWelcome to ${site.name}. ", map));
}
I should also mention that I have no idea what Velocity is, so I hope this answer is relevant.
Solution 5
Here's an outline of how you could go about doing this. It should be relatively straightforward to implement it as actual code.
- Create a map of all the objects that will be referenced in the template.
- Use a regular expression to find variable references in the template and replace them with their values (see step 3). The Matcher class will come in handy for find-and-replace.
- Split the variable name at the dot.
user.name
would becomeuser
andname
. Look upuser
in your map to get the object and use reflection to obtain the value ofname
from the object. Assuming your objects have standard getters, you will look for a methodgetName
and invoke it.
Related videos on Youtube
Joe
Updated on July 08, 2022Comments
-
Joe almost 2 years
Is there any
String
replacement mechanism in Java, where I can pass objects with a text, and it replaces the string as it occurs.
For example, the text is :Hello ${user.name}, Welcome to ${site.name}.
The objects I have are
"user"
and"site"
. I want to replace the strings given inside${}
with its equivalent values from the objects. This is same as we replace objects in a velocity template.-
Droo over 13 yearsReplace where? A class? A JSP? String has a format method if you just:
String.format("Hello %s", username);
-
Adeel Ansari over 13 years@Droo: In the example, string is like
Hello ${user.name}
, not like,Hello %s
orHello {0}
. -
user2012801 over 13 yearsIf you need something that looks like velocity and smells like velocity, maybe it is velocity? :)
-
Joe over 13 years@Droo: Its not a class. I've the above text in a "String" variable and wants to replace all the occurrences of the strings inside ${} with values in the corresponding objects. for example replace all ${user.name} with name property in "user" object.
-
Joe over 13 years@serg: Yes it is a velocity code. and I wants to remove the velocity from my code.
-
AlikElzin-kilaka about 9 yearsPossible duplicate to: stackoverflow.com/q/772988/435605
-
Joe almost 9 years@AlikElzin-kilaka This is not a duplicate, since both posts have its own differences in requirement(though both are string replacements). Please refer the accepted answer to know what I meant.
-
Vishnoo Rath over 6 yearsMaybe a bit late, but please see jeval.sourceforge.net. I have used this in a Grails application. It is a Java library. Very useful and covers more use cases than you need. It is a Formula evaluator as well.
-
-
jjnguy over 13 yearsHeh, just saw this answer. It is identical to mine. Please let me know what you think of my implementation.
-
Joe over 13 yearsThis is what I was looking for. Thank you for giving an implementation. I was trying for it and getting incorrect results. :D. Anyway it solved my problem.
-
jjnguy over 13 years@Joe, glad I could help. It was a good excuse for me to finally practice writing some code that uses reflection in Java.
-
Paul about 13 yearsJavadoc for StrSubstitutor commons.apache.org/lang/api-release/org/apache/commons/lang/…
-
andrewrjones over 10 yearsShould be
Map<String, String> valuesMap = new HashMap<String, String>();
. -
Joseph Rajeev Motha over 10 yearsThanks! I knew java should have an inbuilt way to do this without having to use are freaking template engine to do such a simple thing!
-
Lukuluba over 6 years
StrSubstitutor
is now deprecated in https://commons.apache.org/proper/commons-lang/. User https://commons.apache.org/proper/commons-text/ instead -
Noumenon over 6 yearsSeems like String.format can do anything this can do -- stackoverflow.com/questions/2809633/…
-
davnicwil over 6 years+1. Be aware
format
also takes anObject...
varargs so you can use this more terse syntax where preferableformat("{0} world {1}", "Hello", "!");
-
Yurii Rabeshko over 5 years
StrSubstitutor
deprecated as of 1.3, useStringSubstitutor
instead. This class will be removed in 2.0. Gradle dependency for importingStringSubstitutor
isorg.apache.commons:commons-text:1.4
-
Gaurav over 5 yearsdoes it allow condition based substitution?
-
Marnes almost 5 yearsIt should be noted that
MessageFormat
can only be reliably used for its namesake, display messages, not for output where technical formatting matters. Numbers for example will be formatted per locale settings, rendering them invalid for technical uses. -
asherbret almost 4 yearsThis is less useful when you need to repeat the same argument more than once. E.g.:
String.format("Hello! My name is %s, I'm %s. Why is my name %s you ask? Well I'm only %s years old so I don't know", name, age, name, age);
. Other answers here require specifying each argument only once. -
jazzpi almost 4 years@asherbar you can use argument index specifiers in the format string, e.g.
String.format("Hello! My name is %1$s, I'm %2$s. Why is my name %1$s you ask? Well I'm only %2$s years old so I don't know", name, age)
-
asherbret almost 4 years@jazzpi I never knew that. Thanks!
-
Hikaru Shindo almost 4 yearsI got an error The import java.util.HashMap cannot be resolved
-
aristotll over 3 yearsall links are dead
-
Nick Legend about 3 yearsJust a remark.
StringSubstitutor
instance is created with a substitution map and then parses template strings with itsreplace
method. That means it cannot pre-parse the template string, so processing the same template with different substitution maps may be less efficient. -
Nick Legend about 3 yearsSee this answer for more details.
-
Ari Singh almost 3 yearsNice Utility. Since java 9, you can now inline the map and pass the populated map as parameter e.g. Map.of ("user", user, "site", site), instead of creating the map separately. This will further simplify calling the "format" method.
-
Christoffer Soop over 2 yearsWell, the answer was given over 10 years ago and I just updated the only dead link... :-)
-
Lluis Martinez about 2 yearsVery similar to MessageFormat.format