how to use Java to replace String?(regex) - java

I hava one jsonString
String target = "[{"nickName":"andy","password":"wei","userword":"weitest32123"}]";
I wish to get
String target = "[{"nickName":"andy","password":"xxx","userword":"xxx"}]";
I want to use the java String method replaceall(regex,"xxx");
how do?

Try this.
String input = "[{\"nickName\":\"andy\",\"password\":\"wei\",\"userword\":\"weitest32123\"}]";
String output = input.replaceAll("(?<=\"(pass|user)word\":\")[^\"]+", "xxx");
System.out.println(output);
output:
[{"nickName":"andy","password":"xxx","userword":"xxx"}]

You can do it with the Positive Lookbehind regexp regex101.com:
public static void main(String... args) {
String str = "\"[{\"nickName\":\"andy\",\"password\":\"wei\",\"userword\":\"weitest32123\"}]\"";
System.out.println(maskPassword(str)); // "[{"nickName":"andy","password":"xxx","userword":"xxx"}]"
}
public static String maskPassword(String str) {
String regex = "(?<=\"(password|userword)\":)(\"[^\"]+\")";
return str.replaceAll(regex, "\"xxx\"");
}
P.S. I strongly recommend you not to do this with json string. This could follow a problem in the future. It's better to parse this json string into an object and then create json back with modification.
E.g. you can use a tool that I wrote gson-utils

This may seem like you're trying to parse some JSON. I'm not sure if casting it to object and then hiding values would be better approach.
But if you really need to do this way, this would be the solution
// You need this 2 imports
//import java.util.regex.Matcher;
//import java.util.regex.Pattern;
String text = "[{\"nickName\":\"andy\",\"password\":\"wei\",\"userword\":\"weitest32123\"}]";
Pattern pattern = Pattern.compile("\"(?<key>password|userword)\":\"(?<value>.*?)\"");
Matcher matcher = pattern.matcher(text);
String result = matcher.replaceAll("\"${key}\":\"xxx\"");
In Regex you need to specify all keys that you want to mask

Related

how to remove the slashes from json key while converting object into string

I'm trying to convert JSON object into string by doing the below
JSONObject object = new JSONObject();
object.put("video", data);
array1.add( object.toString().replace("\\\\"," "));
Actual result
["{\"photos\":\"/contests/1/images/1.png\"}",
{\"photos\":\"/contests/1/images/2.png\"}"]
Expected result
["{"photos":"/contests/1/images/1.png\"}","
{"photos":"/contests/1/images/2.png\"}"]
not able to remove the slashes from key
Use replaceAll instead of replace
replaceAll("\\\\", "")
When you want to replace all the occurences with .replace, the first parameter must be a regex, if you supply a string, only the first occurrence will be replaced, that's why your replace wouldn't work.
Please use:
.replace("/\\/g","")
Alternatively, replaceAll can be used as #Code_Mono suggested
The Code_Mode mentioned is correct one.
Because String is immutable. Make sure that you put it right place.
You can refer code bellow for more detail:
public static void main(String[] args) {
String json = "[\"{\\\"photos\\\":\\\"/contests/1/images/1.png\\\"}\", {\\\"photos\\\":\\\"/contests/1/images/2.png\\\"}\"]";
json.replaceAll("\\\\","");
System.out.println(json);
String jsonReplace = json.replaceAll("\\\\","");
System.out.println(jsonReplace);
}
Output value:
["{\"photos\":\"/contests/1/images/1.png\"}", {\"photos\":\"/contests/1/images/2.png\"}"]
["{"photos":"/contests/1/images/1.png"}", {"photos":"/contests/1/images/2.png"}"]

Split JSON objects in array string using regex

I have a String in the following format:
[{"HostName":"taskmanager1","Rack":"/default-rack","State":"RUNNING","NodeId":"taskmanager1:45454","NodeHTTPAddress":"taskmanager1:8042","LastHealthUpdate":1519568501615,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},{"HostName":"datanode2","Rack":"/default-rack","State":"RUNNING","NodeId":"datanode2:45454","NodeHTTPAddress":"datanode2:8042","LastHealthUpdate":1519260876106,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},{"HostName":"taskmanager3","Rack":"/default-rack","State":"RUNNING","NodeId":"taskmanager3:45454","NodeHTTPAddress":"taskmanager3:8042","LastHealthUpdate":1519568502251,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},{"HostName":"datanode3","Rack":"/default-rack","State":"RUNNING","NodeId":"datanode3:45454","NodeHTTPAddress":"datanode3:8042","LastHealthUpdate":1519260871527,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},{"HostName":"taskmanager2","Rack":"/default-rack","State":"RUNNING","NodeId":"taskmanager2:45454","NodeHTTPAddress":"taskmanager2:8042","LastHealthUpdate":1519568502259,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},{"HostName":"datanode1","Rack":"/default-rack","State":"RUNNING","NodeId":"datanode1:45454","NodeHTTPAddress":"datanode1:8042","LastHealthUpdate":1519260875647,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024}]
I want to split it into multiple (here 6) JSON format, but my pattern cannot split that as desired.
I want something like this:
{"HostName":"taskmanager1","Rack":"/default-rack","State":"RUNNING","NodeId":"taskmanager1:45454","NodeHTTPAddress":"taskmanager1:8042","LastHealthUpdate":1519568501615,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},
{"HostName":"datanode2","Rack":"/default-rack","State":"RUNNING","NodeId":"datanode2:45454","NodeHTTPAddress":"datanode2:8042","LastHealthUpdate":1519260876106,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},
{"HostName":"taskmanager3","Rack":"/default-rack","State":"RUNNING","NodeId":"taskmanager3:45454","NodeHTTPAddress":"taskmanager3:8042","LastHealthUpdate":1519568502251,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},
{"HostName":"datanode3","Rack":"/default-rack","State":"RUNNING","NodeId":"datanode3:45454","NodeHTTPAddress":"datanode3:8042","LastHealthUpdate":1519260871527,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024}
,{"HostName":"taskmanager2","Rack":"/default-rack","State":"RUNNING","NodeId":"taskmanager2:45454","NodeHTTPAddress":"taskmanager2:8042","LastHealthUpdate":1519568502259,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024},
{"HostName":"datanode1","Rack":"/default-rack","State":"RUNNING","NodeId":"datanode1:45454","NodeHTTPAddress":"datanode1:8042","LastHealthUpdate":1519260875647,"HealthReport":"","NodeManagerVersion":"2.8.3","NumContainers":0,"UsedMemoryMB":0,"AvailableMemoryMB":1024}
Using the code:
List<String> res = Arrays.asList(temp.replace('[', ' ').replace(']',' ').trim()).split(",");
It will be split for every , character and using the pattern split("},\\}") will remove } and { character, too.
How can I split that as desire to make Json objects?
Using the Java pattern (\\{.+}) will group whole string.
You can parse the JSON as an array and treat the contents as individual strings. Here is sample code:
import org.json.JSONArray;
public class orgJson1Main {
private static final String sample = "[{\"HostName\":\"taskmanager1\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"taskmanager1:45454\",\"NodeHTTPAddress\":\"taskmanager1:8042\",\"LastHealthUpdate\":1519568501615,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"datanode2\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"datanode2:45454\",\"NodeHTTPAddress\":\"datanode2:8042\",\"LastHealthUpdate\":1519260876106,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"taskmanager3\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"taskmanager3:45454\",\"NodeHTTPAddress\":\"taskmanager3:8042\",\"LastHealthUpdate\":1519568502251,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"datanode3\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"datanode3:45454\",\"NodeHTTPAddress\":\"datanode3:8042\",\"LastHealthUpdate\":1519260871527,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"taskmanager2\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"taskmanager2:45454\",\"NodeHTTPAddress\":\"taskmanager2:8042\",\"LastHealthUpdate\":1519568502259,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"datanode1\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"datanode1:45454\",\"NodeHTTPAddress\":\"datanode1:8042\",\"LastHealthUpdate\":1519260875647,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024}]";
public static void main(String[] args) {
JSONArray array = new JSONArray(sample);
for(int i=0; i < array.length(); i++){
System.out.println(array.get(i));
}
}
}
OUTPUT:
{"NodeManagerVersion":"2.8.3","Rack":"/default-rack","LastHealthUpdate":1519568501615,"HealthReport":"","State":"RUNNING","AvailableMemoryMB":1024,"NodeId":"taskmanager1:45454","UsedMemoryMB":0,"NodeHTTPAddress":"taskmanager1:8042","HostName":"taskmanager1","NumContainers":0}
{"NodeManagerVersion":"2.8.3","Rack":"/default-rack","LastHealthUpdate":1519260876106,"HealthReport":"","State":"RUNNING","AvailableMemoryMB":1024,"NodeId":"datanode2:45454","UsedMemoryMB":0,"NodeHTTPAddress":"datanode2:8042","HostName":"datanode2","NumContainers":0}
{"NodeManagerVersion":"2.8.3","Rack":"/default-rack","LastHealthUpdate":1519568502251,"HealthReport":"","State":"RUNNING","AvailableMemoryMB":1024,"NodeId":"taskmanager3:45454","UsedMemoryMB":0,"NodeHTTPAddress":"taskmanager3:8042","HostName":"taskmanager3","NumContainers":0}
{"NodeManagerVersion":"2.8.3","Rack":"/default-rack","LastHealthUpdate":1519260871527,"HealthReport":"","State":"RUNNING","AvailableMemoryMB":1024,"NodeId":"datanode3:45454","UsedMemoryMB":0,"NodeHTTPAddress":"datanode3:8042","HostName":"datanode3","NumContainers":0}
{"NodeManagerVersion":"2.8.3","Rack":"/default-rack","LastHealthUpdate":1519568502259,"HealthReport":"","State":"RUNNING","AvailableMemoryMB":1024,"NodeId":"taskmanager2:45454","UsedMemoryMB":0,"NodeHTTPAddress":"taskmanager2:8042","HostName":"taskmanager2","NumContainers":0}
{"NodeManagerVersion":"2.8.3","Rack":"/default-rack","LastHealthUpdate":1519260875647,"HealthReport":"","State":"RUNNING","AvailableMemoryMB":1024,"NodeId":"datanode1:45454","UsedMemoryMB":0,"NodeHTTPAddress":"datanode1:8042","HostName":"datanode1","NumContainers":0}
EDIT:
First, I removed the JSONTokener from the above code. Second, for completeness I'm adding the following code that shows how to find the individual JSON objects within the sample string using a regex as originally asked.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class orgJson1Main {
private static final String sample = "[{\"HostName\":\"taskmanager1\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"taskmanager1:45454\",\"NodeHTTPAddress\":\"taskmanager1:8042\",\"LastHealthUpdate\":1519568501615,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"datanode2\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"datanode2:45454\",\"NodeHTTPAddress\":\"datanode2:8042\",\"LastHealthUpdate\":1519260876106,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"taskmanager3\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"taskmanager3:45454\",\"NodeHTTPAddress\":\"taskmanager3:8042\",\"LastHealthUpdate\":1519568502251,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"datanode3\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"datanode3:45454\",\"NodeHTTPAddress\":\"datanode3:8042\",\"LastHealthUpdate\":1519260871527,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"taskmanager2\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"taskmanager2:45454\",\"NodeHTTPAddress\":\"taskmanager2:8042\",\"LastHealthUpdate\":1519568502259,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024},{\"HostName\":\"datanode1\",\"Rack\":\"/default-rack\",\"State\":\"RUNNING\",\"NodeId\":\"datanode1:45454\",\"NodeHTTPAddress\":\"datanode1:8042\",\"LastHealthUpdate\":1519260875647,\"HealthReport\":\"\",\"NodeManagerVersion\":\"2.8.3\",\"NumContainers\":0,\"UsedMemoryMB\":0,\"AvailableMemoryMB\":1024}]";
public static void main(String[] args) {
Matcher matcher = Pattern.compile("\\{[^}]*\\}").matcher(sample);
while(matcher.find()){
System.out.println(matcher.group());
}
}
}
To split on }, {, but retain the curly brackets in the tokens, split on this regex:
"(?<=\\}), (?=\\{)"
Which uses a look behind and a look ahead to assert the curly brackets preceed and follow the comma, but not consume them in the split.
The whole line then becomes:
List<String> res = Arrays.asList(temp.replaceAll("^.|.$", "").split("(?<=\\}), (?=\\{)");
Note also the simplified trimming of leading [ and trailing ] but more-simply removing the first and last character in one operation.
If your purpose to use this List as list of MyJsonObject I would recommend to reuse brilliant google gson library.
There is easy way to convert String to List without intermediate manipulation with List.
What you need to follow followed steps.
1) Create your POJO class:
public class POJO
{
String HostName;
String Rack;
String State;
String NodeId;
String NodeHTTPAddress;
String LastHealthUpdate;
String HealthReport;
String NodeManagerVersion;
String NumContainers;
String UsedMemoryMB;
String AvailableMemoryMB;
... getters/setters here ....
}
2) Create gson converter:
Gson gson = (new GsonBuilder() ).create();
3) Create typeToken for list of your POJOs:
Type type = new TypeToken< List<POJO> >(){}.getType();
4) Convert String to desire collection:
List<MyJsonObject> list = gson.fromJson( json, type );

Replace a Param in QueryString in Java

lets say I have a url param like token=1234235asdjaklj231k209a&name=sam&firname=Mahan
how can I replace the value of the token with new one ?
I've done something similar to this with pattern and matcher before but I don't recall now
but I know there is a way to do so
Update : the token can contain any letter but &
thanks in advance
Spring has a util that handles this need gracefully. Apache httpcomponents does too. Below is a spring example.
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
public class StackOverflow {
private static class SO46303058 {
public static void main(String[] args) {
final String urlString = "https://subdomain.hostname/path/resource?token=1234235asdjaklj231k209a&name=sam&firname=Mahan";
final URI uri = UriComponentsBuilder.fromHttpUrl(urlString)
.replaceQueryParam("token", "abc")
.build().toUri();
System.out.println(uri);
}
}
}
Don't be afraid of adding dependencies to your project, it beats reinventing the wheel.
We can consider doing a simple regex replacement, with a few caveats (q.v. below the code snippet).
String url = "token=1234235asdjaklj231k209a&name=sam&firname=Mahan";
url = url.replaceFirst("\\btoken=.*?(&|$)", "token=new_value$1");
System.out.println(url);
url = "param1=value&token=1234235asdjaklj231k209a";
url = url.replaceFirst("\\btoken=.*?(&|$)", "token=new_value$1");
System.out.println(url);
Edge cases to consider are first that your token may be the last parameter in the query string. To cover this case, we should check for token=... ending in either an ambersand & or the end of the string. But if we don't use a lookahead, and instead consume that ambersand, we have to also add it back in the replacement. The other edge case, correctly caught by #DodgyCodeException in his comment below, is that there be another query parameter which just happens to end in token. To make sure we are really matching our token parameter, we can preface it with a word boundary in the regex, i.e. use \btoken=... to refer to it.
Output:
token=new_value&name=sam&firname=Mahan
param1=value&token=new_value
make a viewModel.
public class veiwModel(){ String token ; // and get and set for exmample }
then use Gson if u have a json text .
Gson gson = new Gson();
yourViewModel = gson.fronJson(jsonText , viewModel.class);
System.out.println(yourViewModel.getToken());

efficient way to parse the file to get a List back ...?

ID Name Department Salary Designation
---------------------------------------
1 Kent Engineering 100000$ senior-engineer
2 Smith null 50,000$ administrator
These are the records of a big file I have. I need to parse this file to retrieve salary and designation. The key has to be ID,Name,Department and if that fails then use ID,Name ..
I can only use Java/groovy to do this... How do I get <Salary,Designation> back... ?
What's the most efficient way to grep it given that i can use only java/groovy
Create a BufferedReader, this will allow you to read the file one line at a time. Then call split("[\\\s]") on each of the lines, this will split it by whitespace.
You may also have to massage the salary field so it can be parsed into a number.
Split each line on whitespace.
scala> val a = "1 Kent Engineering 100000$ senior-engineer"
a: java.lang.String = 1 Kent Engineering 100000$ senior-engineer
scala> a.split("\\s+")
res1: Array[java.lang.String] = Array(1, Kent, Engineering, 100000$, senior-engineer)
Then take the raw string values and parse those. E.g. to convert 50,000$ into the number 50000:
double salary = Double.parseDouble(array[SALARY_INDEX].replaceAll("[$,]",""))
java.lang.Double.parseDouble("50,000$".replaceAll("[$,]",""))
res6: Double = 50000.0
You can parse the file into a HashMap and keep it around as long as you need it.
It has a high up-front cost, but if you're going to use it a lot, then it makes sense.
Then create your key and value objects. Be sure to override equals for your key.
public class Key{
int id;
String name;
#Override
public boolean equals(Object o){...}
}
I'll assume that you're aware of how to read files line-by-line using a BufferedReader as that's an obvious prerequisite for this sort of thing - if not, shout.
The key is the thing that you've failed to specify. What, exactly, is the format of the data - in particular, what are the exact rules for determining where one field ends and the other begins?
If the data is separated by tab characters (and said characters are forbidden from occurring in the data, even if escaped) then the solution is simple:
// Ignoring general error handling and EOF-checking here
final String line = bufferedReader.readLine();
final String[] fields = line.split("\t");
Now you have an array containing the fields on the line, so can just look up fields[3] and fields[4].
If the separator is "any number of spaces", and spaces are entirely disallowed, then you've got a similarly easy situation, where your regex is line.split(" *").
In more complex situations, including ones where separators are allowed to appear if escaped or quoted, you may be better off simply iterating over the line character-by-character and manually building up the set of fields according to the separator rules.
With a pinch of Guava library, the solution is simple and elegant. We can improve the below code by objectifying the key, handling errors etc etc but you can do that yourself
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import com.google.common.base.CharMatcher;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.google.common.io.LineProcessor;
public class FileProcessor
{
private static final Splitter SPLITTER = Splitter.on(CharMatcher.WHITESPACE);
private static final Joiner KEY_BUILDER = Joiner.on("_").skipNulls();
#SuppressWarnings("unchecked")
public static void main(final String[] args) throws IOException
{
Map<String, SalaryAndDesignation> result = Files.readLines(new File("c:/1.txt"), Charsets.ISO_8859_1, new LineProcessor() {
private final Map<String, SalaryAndDesignation> result = Maps.newHashMap();
public Object getResult()
{
return result;
}
public boolean processLine(final String line) throws IOException
{
Iterator<String> columns = SPLITTER.split(line).iterator();
String id = columns.next();
String name = columns.next();
String dept = columns.next();
String key = KEY_BUILDER.join(id, name, "null".equals(dept) ? null : dept);
result.put(key, new SalaryAndDesignation(columns.next(), columns.next()));
return true;
}
});
System.out.println(result.size());
}
final static class SalaryAndDesignation
{
String salary;
String designation;
public SalaryAndDesignation(final String salary, final String designation)
{
super();
this.salary = salary;
this.designation = designation;
}
}
}
I see using groovy results in lesser code as sample below (copy/pasted from here) but never used it myself
new File("simple.tab").withReader{r->
line = r.readLine();
println "first line: $line"
r.splitEachLine("\t"){fields->
println "fields on line: $fields"
}
}

String replacement in java, similar to a velocity template

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.
Use StringSubstitutor from Apache Commons Text.
Dependency import
Import the Apache commons text dependency using maven as bellow:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
Example
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);
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);
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
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.
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 become user and name. Look up user in your map to get the object and use reflection to obtain the value of name from the object. Assuming your objects have standard getters, you will look for a method getName and invoke it.
There are a couple of Expression Language implementations out there that does this for you, could be preferable to using your own implementation as or if your requirments grow, see for example JUEL and MVEL
I like and have successfully used MVEL in at least one project.
Also see the Stackflow post JSTL/JSP EL (Expression Language) in a non JSP (standalone) context
Handlebars.java might be a better option in terms of a Velocity-like syntax with other server-side templating features.
http://jknack.github.io/handlebars.java/
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hello {{this}}!");
System.out.println(template.apply("Handlebars.java"));
I use GroovyShell in java to parse template with Groovy GString:
Binding binding = new Binding();
GroovyShell gs = new GroovyShell(binding);
// this JSONObject can also be replaced by any Java Object
JSONObject obj = new JSONObject();
obj.put("key", "value");
binding.setProperty("obj", obj)
String str = "${obj.key}";
String exp = String.format("\"%s\".toString()", str);
String res = (String) gs.evaluate(exp);
// value
System.out.println(str);
I created this utility that uses vanilla Java. It combines two formats... {} and %s style from String.format.... into one method call. Please note it only replaces empty {} brackets, not {someWord}.
public class LogUtils {
public static String populate(String log, Object... objects) {
log = log.replaceAll("\\{\\}", "%s");
return String.format(log, objects);
}
public static void main(String[] args) {
System.out.println(populate("x = %s, y ={}", 5, 4));;
}
}
Since Java 15 you have the method String.formatted() (see documentation).
str.formatted(args) is the equivalent of String.format(str, args) with less ceremony.
For the example mentioned in the question, the method could be used as follows:
"Hello %s, Welcome to %s.".formatted(user.getName(), site.getName())
Good news. Java is most likely going to have string templates (probably from version 21).
See the string templates proposal (JEP 430) here.
It will be something along the lines of this:
String name = "John";
String info = STR."I am \{name}";
System.out.println(info); // I am John
P.S. Kotlin is 100% interoperable with Java. It supports cleaner string templates out of the box:
val name = "John"
val info = "I am $name"
println(info) // I am John
Combined with extension functions, you can achieve the same thing the Java template processors (e.g. STR) will do.
There is nothing out of the box that is comparable to velocity since velocity was written to solve exactly that problem. The closest thing you can try is looking into the Formatter
http://cupi2.uniandes.edu.co/site/images/recursos/javadoc/j2se/1.5.0/docs/api/java/util/Formatter.html
However the formatter as far as I know was created to provide C like formatting options in Java so it may not scratch exactly your itch but you are welcome to try :).

Categories

Resources