Java finding substring - java

I have the following String:
oauth_token=safcanhpyuqu96vfhn4w6p9x&**oauth_token_secret=hVhzHVVMHySB**&application_name=Application_Name&login_url=https%3A%2F%2Fapi-user.netflix.com%2Foauth%2Flogin%3Foauth_token%3Dsafcanhpyuqu96vfhn4w6p9x
I am trying to parse out the value for oauth_token_secret. I need everything from the equals sign (=) to the next ampersand sign (&). So I need to parse out: hVhzHVVMHySB
Currently, I have the following code:
Const.OAUTH_TOKEN_SECRET = "oauth_token_secret";
Const.tokenSecret =
content.substring(content.indexOf((Const.OAUTH_TOKEN_SECRET + "="))
+ (Const.OAUTH_TOKEN_SECRET + "=").length(),
content.length());
This will start at the beginning of the oauth_token_string, but it will not stop at the next ampersand. I am unsure how to specify to stop at the end of the following ampersand. Can anyone help me?

The indexOf() methods allow you to specify an optional fromIndex. This allows you to find the next ampersand:
int oauth = content.indexOf(Const.OAUTH_TOKEN_SECRET);
if (oauth != -1) {
int start = oath + Const.OATH_TOKEN_SECRET.length(); // or
//int start = content.indexOf('=', oath) + 1;
int end = content.indexOf('&', start);
String tokenSecret = end == -1 ? content.substring(start) : content.substring(start, end);
}

public static Map<String, String> buildQueryMap(String query)
{
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params)
{
String[] pair = param.split("=");
String name = pair[0];
String value = pair[1];
map.put(name, value);
}
return map;
}
// in your code
Map<String, String> queryMap = buildQueryMap("a=1&b=2&c=3....");
String tokenSecret = queryMap.get(Const.OAUTH_TOKEN_SECRET);

Using String.split gives a much cleaner solution.
static String getValue(String key, String content) {
String[] tokens = content.split("[=&]");
for(int i = 0; i < tokens.length - 1; ++i) {
if(tokens[i].equals(key)) {
return tokens[i+1];
}
}
return null;
}
Click here for a test drive! ;-)

A much better solution is using the Pattern and corresponding Matcher class.
By using a capturing group you can check and "cut out" the the appropriate substring in one step.

Related

Get parameter after "#" from url java

I have a redirect uri of the form https://stackexchange.com/oauth/login_success#access_token=token&expires=5678. I am trying to get the acces token from this url. tried following methods
uri.getQueryParameter("access_token"); //will return null since it is not a query param
uri.getFragment(); //will return "access_token=token&expires=5678" so i need to seperate it again.
Any direct methods? Pls help
Some one might find this helpful
String queryAfterFragment = uri.getFragment();
String dummy_url = "http://localhost?" + queryAfterFragment;
Uri dummy_uri = Uri.parse(dummy_url);
String access_token = dummy_uri.getQueryParameter("access_token");
Works like a charm and easy to use, thank me later :-)
Simple and elegant solution which can get the values which you want:
public static Map<String, String> parseUrlFragment (String url) {
Map<String, String> output = new LinkedHashMap<> ();
String[] keys = url.split ("&");
for (String key : keys) {
String[] values = key.split ("=");
output.put (values[0], (values.length > 1 ? values[1] : ""));
}
return output;
}
It's using LinkedHashMap to represent values, so it's output:
Map<String, String> data = parseUrlFragment (uri.getFragment ());
data.get ("access_token") // token
data.get ("expires") // 5678
You can try in this way
String str = "https://stackexchange.com/oauth/
login_success#access_token=token&expires=5678";
int indexOfHash = str.indexOf("#");
// now you can substring from this
String subStr = str.substring(indexOfHash+1, str.length());
System.out.println(subStr);
// now you can substring from &
String sStr=subStr.substring(0,subStr.indexOf("&"));
System.out.println(sStr);
// now you can get token
String[] arr=sStr.split("=");
System.out.println(arr[0]);
System.out.println(arr[1]);
Out put
access_token=token&expires=5678
access_token=token
access_token
token
You could use the String method split(String) with Regex
str.split("#|&|=")
this splits the string by the passed 3 chars and you get an array with all the splitted parts.
String s =
"https://stackexchange.com/oauth/login_success#access_token=token&expires=5678";
final String[] split = s.split("#|&|=");
for (String s1 : split) {
System.out.println(s1);
}
Output:
https://stackexchange.com/oauth/login_success
access_token
token
expires
5678

How to sort a string into a map and print the results

I have a string in the format nm=Alan&hei=72&hair=brown
I would like to split this information up, add a conversion to the first value and print the results in the format
nm Name Alan
hei Height 72
hair Hair Color brown
I've looked at various methods using the split function and hashmaps but have had no luck piecing it all together.
Any advice would be very useful to me.
Map<String, String> aliases = new HashMap<String, String>();
aliases.put("nm", "Name");
aliases.put("hei", "Height");
aliases.put("hair", "Hair Color");
String[] params = str.split("&"); // gives you string array: nm=Alan, hei=72, hair=brown
for (String p : params) {
String[] nv = p.split("=");
String name = nv[0];
String value = nv[1];
System.out.println(nv[0] + " " + aliases.get(nv[0]) + " " + nv[1]);
}
I really do not understand what you problem was...
Try something like this:
static final String DELIMETER = "&"
Map<String,String> map = ...
map.put("nm","Name");
map.put("hei","Height");
map.put("hair","Hair color");
StringBuilder builder = new StringBuilder();
String input = "nm=Alan&hei=72&hair=brown"
String[] splitted = input.split(DELIMETER);
for(Stirng str : splitted){
int index = str.indexOf("=");
String key = str.substring(0,index);
builder.append(key);
builder.append(map.get(key));
builder.append(str.substring(index));
builder.append("\n");
}
A HashMap consists of many key, value pairs. So when you use split, devise an appropriate regex (&). Once you have your string array, you can use one of the elements as the key (think about which element will make the best key). However, you may now be wondering- "how do I place the rest of elements as the values?". Perhaps you can create a new class which stores the rest of the elements and use objects of this class as values for the hashmap.
Then printing becomes easy- merely search for the value of the corresponding key. This value will be an object; use the appropriate method on this object to retrieve the elements and you should be able to print everything.
Also, remember to handle exceptions in your code. e.g. check for nulls, etc.
Another thing: your qn mentions the word "sort". I don't fully get what that means in this context...
Map<String, String> propsMap = new HashMap<String, String>();
Map<String, String> propAlias = new HashMap<String, String>();
propAlias.put("nm", "Name");
propAlias.put("hei", "Height");
propAlias.put("hair", "Hair Color");
String[] props = input.split("&");
if (props != null && props.length > 0) {
for (String prop : props) {
String[] propVal = prop.split("=");
if (propVal != null && propVal.length == 2) {
propsMap.put(propVal[0], propVal[1]);
}
}
}
for (Map.Entry tuple : propsMap.getEntrySet()) {
if (propAlias.containsKey(tuple.getKey())) {
System.out.println(tuple.getKey() + " " + propAlias.get(tuple.getKey()) + " " + tuple.getValue());
}
}

Simplest way to strip an int out of a URL in Java?

I have a String containing a URL. I want to get just one piece of data out of it: an int that should be showing up in the query string.
So if the url is:
http://domain.tld/page.html?iVar=123
I want to get "123" into an int.
What's the most elegant way you know to do this?
You could try matching just that parameter in the URL string:
public static Integer getIVarParamValue(String urlStr) {
Pattern p = Pattern.compile("iVar=(\\d+)");
Matcher m = p.matcher(urlStr);
if (m.find()) {
return Integer.parseInt(m.group(1));
}
return null;
}
It seems you want to obtain get parameters and parse them. I have this method here (got it from somewhere on SO, I guess):
public static Map<String, List<String>> getQueryParams(String url) {
try {
Map<String, List<String>> params = new HashMap<String, List<String>>();
String[] urlParts = url.split("\\?");
if (urlParts.length > 1) {
String query = urlParts[1];
for (String param : query.split("&")) {
String[] pair = param.split("=");
String key = URLDecoder.decode(pair[0], "UTF-8");
String value = "";
if (pair.length > 1) {
value = URLDecoder.decode(pair[1], "UTF-8");
}
List<String> values = params.get(key);
if (values == null) {
values = new ArrayList<String>();
params.put(key, values);
}
values.add(value);
}
}
return params;
} catch (UnsupportedEncodingException ex) {
throw new AssertionError(ex);
}
}
So:
String var = WebUtils.getQueryParams(url).get("iVar");
int intVar = Integer.parseInt(var);
You can use the URL class.
i.e.:
URL myUrl = new URL("http://domain.tld/page.html?iVar=123");
String query = myUrl.getQuery(); //this will return iVar=123
//at this point you can either parse it manually (i.e. use some of the regexp in the other suggestions, or use something like:
String[] parts = query.split();
String variable = parts[0];
String value = parts[1];
This will work only for this case though and won't work if you have additional params or no params.
There are a number of solution that will split it into a param map online, you can see some here.
If it's really as simple as you describe: There is only 1 int in your URL and all you want is that int, I'd go for a regular expression. If it is actually more complicated see the other answers.
Pattern p = Pattern.compile("[0-9]+");
Matcher m = p.matcher("http://domain.tld/page.html?iVar=123");
if (m.find())
System.out.println(m.group());
This also could do the work :
public static int getIntParam(HttpServletRequest request, String name, int defaultValue) {
String value = request.getParameter(name);
try {
if (value != null) {
return Integer.valueOf(value);
}
} catch (NumberFormatException e) {
}
return defaultValue;
}
Hope it helps!
If the query string part of the URL is always the same (so if it was always iVar) you could use urlAsString.indexOf("iVar=") to find iVar= and then knowing the number is after that, extract the number. That is admittedly not the least brittle approach.
But if you're looking for all the query strings then Bozho's answer is much better.

Named placeholders in string formatting

In Python, when formatting string, I can fill placeholders by name rather than by position, like that:
print "There's an incorrect value '%(value)s' in column # %(column)d" % \
{ 'value': x, 'column': y }
I wonder if that is possible in Java (hopefully, without external libraries)?
StrSubstitutor of jakarta commons lang is a light weight way of doing this provided your values are already formatted correctly.
http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/text/StrSubstitutor.html
Map<String, String> values = new HashMap<String, String>();
values.put("value", x);
values.put("column", y);
StrSubstitutor sub = new StrSubstitutor(values, "%(", ")");
String result = sub.replace("There's an incorrect value '%(value)' in column # %(column)");
The above results in:
"There's an incorrect value '1' in column # 2"
When using Maven you can add this dependency to your pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
not quite, but you can use MessageFormat to reference one value multiple times:
MessageFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);
The above can be done with String.format() as well, but I find messageFormat syntax cleaner if you need to build complex expressions, plus you dont need to care about the type of the object you are putting into the string
Another example of Apache Common StringSubstitutor for simple named placeholder.
String template = "Welcome to {theWorld}. My name is {myName}.";
Map<String, String> values = new HashMap<>();
values.put("theWorld", "Stackoverflow");
values.put("myName", "Thanos");
String message = StringSubstitutor.replace(template, values, "{", "}");
System.out.println(message);
// Welcome to Stackoverflow. My name is Thanos.
You can use StringTemplate library, it offers what you want and much more.
import org.antlr.stringtemplate.*;
final StringTemplate hello = new StringTemplate("Hello, $name$");
hello.setAttribute("name", "World");
System.out.println(hello.toString());
public static String format(String format, Map<String, Object> values) {
StringBuilder formatter = new StringBuilder(format);
List<Object> valueList = new ArrayList<Object>();
Matcher matcher = Pattern.compile("\\$\\{(\\w+)}").matcher(format);
while (matcher.find()) {
String key = matcher.group(1);
String formatKey = String.format("${%s}", key);
int index = formatter.indexOf(formatKey);
if (index != -1) {
formatter.replace(index, index + formatKey.length(), "%s");
valueList.add(values.get(key));
}
}
return String.format(formatter.toString(), valueList.toArray());
}
Example:
String format = "My name is ${1}. ${0} ${1}.";
Map<String, Object> values = new HashMap<String, Object>();
values.put("0", "James");
values.put("1", "Bond");
System.out.println(format(format, values)); // My name is Bond. James Bond.
Thanks for all your help! Using all your clues, I've written routine to do exactly what I want -- python-like string formatting using dictionary. Since I'm Java newbie, any hints are appreciated.
public static String dictFormat(String format, Hashtable<String, Object> values) {
StringBuilder convFormat = new StringBuilder(format);
Enumeration<String> keys = values.keys();
ArrayList valueList = new ArrayList();
int currentPos = 1;
while (keys.hasMoreElements()) {
String key = keys.nextElement(),
formatKey = "%(" + key + ")",
formatPos = "%" + Integer.toString(currentPos) + "$";
int index = -1;
while ((index = convFormat.indexOf(formatKey, index)) != -1) {
convFormat.replace(index, index + formatKey.length(), formatPos);
index += formatPos.length();
}
valueList.add(values.get(key));
++currentPos;
}
return String.format(convFormat.toString(), valueList.toArray());
}
This is an old thread, but just for the record, you could also use Java 8 style, like this:
public static String replaceParams(Map<String, String> hashMap, String template) {
return hashMap.entrySet().stream().reduce(template, (s, e) -> s.replace("%(" + e.getKey() + ")", e.getValue()),
(s, s2) -> s);
}
Usage:
public static void main(String[] args) {
final HashMap<String, String> hashMap = new HashMap<String, String>() {
{
put("foo", "foo1");
put("bar", "bar1");
put("car", "BMW");
put("truck", "MAN");
}
};
String res = replaceParams(hashMap, "This is '%(foo)' and '%(foo)', but also '%(bar)' '%(bar)' indeed.");
System.out.println(res);
System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(foo)', but also '%(bar)' '%(bar)' indeed."));
System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(truck)', but also '%(foo)' '%(bar)' + '%(truck)' indeed."));
}
The output will be:
This is 'foo1' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'MAN', but also 'foo1' 'bar1' + 'MAN' indeed.
Apache Commons StringSubstitutor can be used. Note that StrSubstitutor is deprecated.
import org.apache.commons.text.StringSubstitutor;
// ...
Map<String, String> values = new HashMap<>();
values.put("animal", "quick brown fox");
values.put("target", "lazy dog");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("The ${animal} jumped over the ${target}.");
// "The quick brown fox jumped over the lazy dog."
This class supports providing default values for variables.
String result = sub.replace("The number is ${undefined.property:-42}.");
// "The number is 42."
To use recursive variable replacement, call setEnableSubstitutionInVariables(true);.
Map<String, String> values = new HashMap<>();
values.put("b", "c");
values.put("ac", "Test");
StringSubstitutor sub = new StringSubstitutor(values);
sub.setEnableSubstitutionInVariables(true);
String result = sub.replace("${a${b}}");
// "Test"
I am the author of a small library that does exactly what you want:
Student student = new Student("Andrei", 30, "Male");
String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
.arg("id", 10)
.arg("st", student)
.format();
System.out.println(studStr);
Or you can chain the arguments:
String result = template("#{x} + #{y} = #{z}")
.args("x", 5, "y", 10, "z", 15)
.format();
System.out.println(result);
// Output: "5 + 10 = 15"
There is nothing built into Java at the moment of writing this. I would suggest writing your own implementation. My preference is for a simple fluent builder interface instead of creating a map and passing it to function -- you end up with a nice contiguous chunk of code, for example:
String result = new TemplatedStringBuilder("My name is {{name}} and I from {{town}}")
.replace("name", "John Doe")
.replace("town", "Sydney")
.finish();
Here is a simple implementation:
class TemplatedStringBuilder {
private final static String TEMPLATE_START_TOKEN = "{{";
private final static String TEMPLATE_CLOSE_TOKEN = "}}";
private final String template;
private final Map<String, String> parameters = new HashMap<>();
public TemplatedStringBuilder(String template) {
if (template == null) throw new NullPointerException();
this.template = template;
}
public TemplatedStringBuilder replace(String key, String value){
parameters.put(key, value);
return this;
}
public String finish(){
StringBuilder result = new StringBuilder();
int startIndex = 0;
while (startIndex < template.length()){
int openIndex = template.indexOf(TEMPLATE_START_TOKEN, startIndex);
if (openIndex < 0){
result.append(template.substring(startIndex));
break;
}
int closeIndex = template.indexOf(TEMPLATE_CLOSE_TOKEN, openIndex);
if(closeIndex < 0){
result.append(template.substring(startIndex));
break;
}
String key = template.substring(openIndex + TEMPLATE_START_TOKEN.length(), closeIndex);
if (!parameters.containsKey(key)) throw new RuntimeException("missing value for key: " + key);
result.append(template.substring(startIndex, openIndex));
result.append(parameters.get(key));
startIndex = closeIndex + TEMPLATE_CLOSE_TOKEN.length();
}
return result.toString();
}
}
Apache Commons Lang's replaceEach method may come in handy dependeding on your specific needs. You can easily use it to replace placeholders by name with this single method call:
StringUtils.replaceEach("There's an incorrect value '%(value)' in column # %(column)",
new String[] { "%(value)", "%(column)" }, new String[] { x, y });
Given some input text, this will replace all occurrences of the placeholders in the first string array with the corresponding values in the second one.
You should have a look at the official ICU4J library. It provides a MessageFormat class similar to the one available with the JDK but this former supports named placeholders.
Unlike other solutions provided on this page. ICU4j is part of the ICU project that is maintained by IBM and regularly updated. In addition, it supports advanced use cases such as pluralization and much more.
Here is a code example:
MessageFormat messageFormat =
new MessageFormat("Publication written by {author}.");
Map<String, String> args = Map.of("author", "John Doe");
System.out.println(messageFormat.format(args));
As of 2022 the up-to-date solution is Apache Commons Text StringSubstitutor
From the doc:
// Build map
Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target} ${undefined.number:-1234567890} times.";
// Build StringSubstitutor
StringSubstitutor sub = new StringSubstitutor(valuesMap);
// Replace
String resolvedString = sub.replace(templateString)
;
You could have something like this on a string helper class
/**
* An interpreter for strings with named placeholders.
*
* For example given the string "hello %(myName)" and the map <code>
* <p>Map<String, Object> map = new HashMap<String, Object>();</p>
* <p>map.put("myName", "world");</p>
* </code>
*
* the call {#code format("hello %(myName)", map)} returns "hello world"
*
* It replaces every occurrence of a named placeholder with its given value
* in the map. If there is a named place holder which is not found in the
* map then the string will retain that placeholder. Likewise, if there is
* an entry in the map that does not have its respective placeholder, it is
* ignored.
*
* #param str
* string to format
* #param values
* to replace
* #return formatted string
*/
public static String format(String str, Map<String, Object> values) {
StringBuilder builder = new StringBuilder(str);
for (Entry<String, Object> entry : values.entrySet()) {
int start;
String pattern = "%(" + entry.getKey() + ")";
String value = entry.getValue().toString();
// Replace every occurence of %(key) with value
while ((start = builder.indexOf(pattern)) != -1) {
builder.replace(start, start + pattern.length(), value);
}
}
return builder.toString();
}
Based on the answer I created MapBuilder class:
public class MapBuilder {
public static Map<String, Object> build(Object... data) {
Map<String, Object> result = new LinkedHashMap<>();
if (data.length % 2 != 0) {
throw new IllegalArgumentException("Odd number of arguments");
}
String key = null;
Integer step = -1;
for (Object value : data) {
step++;
switch (step % 2) {
case 0:
if (value == null) {
throw new IllegalArgumentException("Null key value");
}
key = (String) value;
continue;
case 1:
result.put(key, value);
break;
}
}
return result;
}
}
then I created class StringFormat for String formatting:
public final class StringFormat {
public static String format(String format, Object... args) {
Map<String, Object> values = MapBuilder.build(args);
for (Map.Entry<String, Object> entry : values.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
format = format.replace("$" + key, value.toString());
}
return format;
}
}
which you could use like that:
String bookingDate = StringFormat.format("From $startDate to $endDate"),
"$startDate", formattedStartDate,
"$endDate", formattedEndDate
);
I created also a util/helper class (using jdk 8) which can format a string an replaces occurrences of variables.
For this purpose I used the Matchers "appendReplacement" method which does all the substitution and loops only over the affected parts of a format string.
The helper class isn't currently well javadoc documented. I will changes this in the future ;)
Anyway I commented the most important lines (I hope).
public class FormatHelper {
//Prefix and suffix for the enclosing variable name in the format string.
//Replace the default values with any you need.
public static final String DEFAULT_PREFIX = "${";
public static final String DEFAULT_SUFFIX = "}";
//Define dynamic function what happens if a key is not found.
//Replace the defualt exception with any "unchecked" exception type you need or any other behavior.
public static final BiFunction<String, String, String> DEFAULT_NO_KEY_FUNCTION =
(fullMatch, variableName) -> {
throw new RuntimeException(String.format("Key: %s for variable %s not found.",
variableName,
fullMatch));
};
private final Pattern variablePattern;
private final Map<String, String> values;
private final BiFunction<String, String, String> noKeyFunction;
private final String prefix;
private final String suffix;
public FormatHelper(Map<String, String> values) {
this(DEFAULT_NO_KEY_FUNCTION, values);
}
public FormatHelper(
BiFunction<String, String, String> noKeyFunction, Map<String, String> values) {
this(DEFAULT_PREFIX, DEFAULT_SUFFIX, noKeyFunction, values);
}
public FormatHelper(String prefix, String suffix, Map<String, String> values) {
this(prefix, suffix, DEFAULT_NO_KEY_FUNCTION, values);
}
public FormatHelper(
String prefix,
String suffix,
BiFunction<String, String, String> noKeyFunction,
Map<String, String> values) {
this.prefix = prefix;
this.suffix = suffix;
this.values = values;
this.noKeyFunction = noKeyFunction;
//Create the Pattern and quote the prefix and suffix so that the regex don't interpret special chars.
//The variable name is a "\w+" in an extra capture group.
variablePattern = Pattern.compile(Pattern.quote(prefix) + "(\\w+)" + Pattern.quote(suffix));
}
public static String format(CharSequence format, Map<String, String> values) {
return new FormatHelper(values).format(format);
}
public static String format(
CharSequence format,
BiFunction<String, String, String> noKeyFunction,
Map<String, String> values) {
return new FormatHelper(noKeyFunction, values).format(format);
}
public static String format(
String prefix, String suffix, CharSequence format, Map<String, String> values) {
return new FormatHelper(prefix, suffix, values).format(format);
}
public static String format(
String prefix,
String suffix,
BiFunction<String, String, String> noKeyFunction,
CharSequence format,
Map<String, String> values) {
return new FormatHelper(prefix, suffix, noKeyFunction, values).format(format);
}
public String format(CharSequence format) {
//Create matcher based on the init pattern for variable names.
Matcher matcher = variablePattern.matcher(format);
//This buffer will hold all parts of the formatted finished string.
StringBuffer formatBuffer = new StringBuffer();
//loop while the matcher finds another variable (prefix -> name <- suffix) match
while (matcher.find()) {
//The root capture group with the full match e.g ${variableName}
String fullMatch = matcher.group();
//The capture group for the variable name resulting from "(\w+)" e.g. variableName
String variableName = matcher.group(1);
//Get the value in our Map so the Key is the used variable name in our "format" string. The associated value will replace the variable.
//If key is missing (absent) call the noKeyFunction with parameters "fullMatch" and "variableName" else return the value.
String value = values.computeIfAbsent(variableName, key -> noKeyFunction.apply(fullMatch, key));
//Escape the Map value because the "appendReplacement" method interprets the $ and \ as special chars.
String escapedValue = Matcher.quoteReplacement(value);
//The "appendReplacement" method replaces the current "full" match (e.g. ${variableName}) with the value from the "values" Map.
//The replaced part of the "format" string is appended to the StringBuffer "formatBuffer".
matcher.appendReplacement(formatBuffer, escapedValue);
}
//The "appendTail" method appends the last part of the "format" String which has no regex match.
//That means if e.g. our "format" string has no matches the whole untouched "format" string is appended to the StringBuffer "formatBuffer".
//Further more the method return the buffer.
return matcher.appendTail(formatBuffer)
.toString();
}
public String getPrefix() {
return prefix;
}
public String getSuffix() {
return suffix;
}
public Map<String, String> getValues() {
return values;
}
}
You can create a class instance for a specific Map with values (or suffix prefix or noKeyFunction)
like:
Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");
FormatHelper formatHelper = new FormatHelper(values);
formatHelper.format("${firstName} ${lastName} is Spiderman!");
// Result: "Peter Parker is Spiderman!"
// Next format:
formatHelper.format("Does ${firstName} ${lastName} works as photographer?");
//Result: "Does Peter Parker works as photographer?"
Further more you can define what happens if a key in the values Map is missing (works in both ways e.g. wrong variable name in format string or missing key in Map).
The default behavior is an thrown unchecked exception (unchecked because I use the default jdk8 Function which cant handle checked exceptions) like:
Map<String, String> map = new HashMap<>();
map.put("firstName", "Peter");
map.put("lastName", "Parker");
FormatHelper formatHelper = new FormatHelper(map);
formatHelper.format("${missingName} ${lastName} is Spiderman!");
//Result: RuntimeException: Key: missingName for variable ${missingName} not found.
You can define a custom behavior in the constructor call like:
Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");
FormatHelper formatHelper = new FormatHelper(fullMatch, variableName) -> variableName.equals("missingName") ? "John": "SOMETHING_WRONG", values);
formatHelper.format("${missingName} ${lastName} is Spiderman!");
// Result: "John Parker is Spiderman!"
or delegate it back to the default no key behavior:
...
FormatHelper formatHelper = new FormatHelper((fullMatch, variableName) -> variableName.equals("missingName") ? "John" :
FormatHelper.DEFAULT_NO_KEY_FUNCTION.apply(fullMatch,
variableName), map);
...
For better handling there are also static method representations like:
Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");
FormatHelper.format("${firstName} ${lastName} is Spiderman!", map);
// Result: "Peter Parker is Spiderman!"
There is Java Plugin to use string interpolation in Java (like in Kotlin, JavaScript). Supports Java 8, 9, 10, 11…​ https://github.com/antkorwin/better-strings
Using variables in string literals:
int a = 3;
int b = 4;
System.out.println("${a} + ${b} = ${a+b}");
Using expressions:
int a = 3;
int b = 4;
System.out.println("pow = ${a * a}");
System.out.println("flag = ${a > b ? true : false}");
Using functions:
#Test
void functionCall() {
System.out.println("fact(5) = ${factorial(5)}");
}
long factorial(int n) {
long fact = 1;
for (int i = 2; i <= n; i++) {
fact = fact * i;
}
return fact;
}
For more info, please read the project README.
The quick answer is no, unfortunately. However, you can come pretty close to a reasonable syntaks:
"""
You are $compliment!
"""
.replace('$compliment', 'awesome');
It's more readable and predictable than String.format, at least!
My answer is to:
a) use StringBuilder when possible
b) keep (in any form: integer is the best, speciall char like dollar macro etc) position of "placeholder" and then use StringBuilder.insert() (few versions of arguments).
Using external libraries seems overkill and I belive degrade performance significant, when StringBuilder is converted to String internally.
Try Freemarker, templating library.
I tried in just a quick way
public static void main(String[] args)
{
String rowString = "replace the value ${var1} with ${var2}";
Map<String,String> mappedValues = new HashMap<>();
mappedValues.put("var1", "Value 1");
mappedValues.put("var2", "Value 2");
System.out.println(replaceOccurence(rowString, mappedValues));
}
private static String replaceOccurence(String baseStr ,Map<String,String> mappedValues)
{
for(String key :mappedValues.keySet())
{
baseStr = baseStr.replace("${"+key+"}", mappedValues.get(key));
}
return baseStr;
}
I ended up with the next solution:
Create class TemplateSubstitutor with method substitute() and use it to format output
Then create a string template and fill it with values
import java.util.*;
public class MyClass {
public static void main(String args[]) {
String template = "WRR = {WRR}, SRR = {SRR}\n" +
"char_F1 = {char_F1}, word_F1 = {word_F1}\n";
Map<String, Object> values = new HashMap<>();
values.put("WRR", 99.9);
values.put("SRR", 99.8);
values.put("char_F1", 80);
values.put("word_F1", 70);
String message = TemplateSubstitutor.substitute(values, template);
System.out.println(message);
}
}
class TemplateSubstitutor {
public static String substitute(Map<String, Object> map, String input_str) {
String output_str = input_str;
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
output_str = output_str.replace("{" + key + "}", String.valueOf(value));
}
return output_str;
}
}
https://dzone.com/articles/java-string-format-examples String.format(inputString, [listOfParams]) would be the easiest way. Placeholders in string can be defined by order. For more details check the provided link.

Parsing query strings on Android

Java EE has ServletRequest.getParameterValues().
On non-EE platforms, URL.getQuery() simply returns a string.
What's the normal way to properly parse the query string in a URL when not on Java EE?
It is popular in the answers to try and make your own parser. This is very interesting and exciting micro-coding project, but I cannot say that it is a good idea.
The code snippets below are generally flawed or broken. Breaking them is an interesting exercise for the reader. And to the hackers attacking the websites that use them.
Parsing query strings is a well defined problem but reading the spec and understanding the nuances is non-trivial. It is far better to let some platform library coder do the hard work, and do the fixing, for you!
On Android:
import android.net.Uri;
[...]
Uri uri=Uri.parse(url_string);
uri.getQueryParameter("para1");
Since Android M things have got more complicated. The answer of android.net.URI.getQueryParameter() has a bug which breaks spaces before JellyBean.
Apache URLEncodedUtils.parse() worked, but was deprecated in L, and removed in M.
So the best answer now is UrlQuerySanitizer. This has existed since API level 1 and still exists. It also makes you think about the tricky issues like how do you handle special characters, or repeated values.
The simplest code is
UrlQuerySanitizer.ValueSanitizer sanitizer = UrlQuerySanitizer.getAllButNullLegal();
// remember to decide if you want the first or last parameter with the same name
// If you want the first call setPreferFirstRepeatedParameter(true);
sanitizer.parseUrl(url);
String value = sanitizer.getValue("paramName");
If you are happy with the default parsing behavior you can do:
new UrlQuerySanitizer(url).getValue("paramName")
but you should make sure you understand what the default parsing behavor is, as it might not be what you want.
On Android, the Apache libraries provide a Query parser:
http://developer.android.com/reference/org/apache/http/client/utils/URLEncodedUtils.html and http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/utils/URLEncodedUtils.html
public static Map<String, List<String>> getUrlParameters(String url)
throws UnsupportedEncodingException {
Map<String, List<String>> params = new HashMap<String, List<String>>();
String[] urlParts = url.split("\\?");
if (urlParts.length > 1) {
String query = urlParts[1];
for (String param : query.split("&")) {
String pair[] = param.split("=", 2);
String key = URLDecoder.decode(pair[0], "UTF-8");
String value = "";
if (pair.length > 1) {
value = URLDecoder.decode(pair[1], "UTF-8");
}
List<String> values = params.get(key);
if (values == null) {
values = new ArrayList<String>();
params.put(key, values);
}
values.add(value);
}
}
return params;
}
If you have jetty (server or client) libs on your classpath you can use the jetty util classes (see javadoc), e.g.:
import org.eclipse.jetty.util.*;
URL url = new URL("www.example.com/index.php?foo=bar&bla=blub");
MultiMap<String> params = new MultiMap<String>();
UrlEncoded.decodeTo(url.getQuery(), params, "UTF-8");
assert params.getString("foo").equals("bar");
assert params.getString("bla").equals("blub");
If you're using Spring 3.1 or greater (yikes, was hoping that support went back further), you can use the UriComponents and UriComponentsBuilder:
UriComponents components = UriComponentsBuilder.fromUri(uri).build();
List<String> myParam = components.getQueryParams().get("myParam");
components.getQueryParams() returns a MultiValueMap<String, String>
Here's some more documentation.
I have methods to achieve this:
1):
public static String getQueryString(String url, String tag) {
String[] params = url.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params) {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
Set<String> keys = map.keySet();
for (String key : keys) {
if(key.equals(tag)){
return map.get(key);
}
System.out.println("Name=" + key);
System.out.println("Value=" + map.get(key));
}
return "";
}
2) and the easiest way to do this Using Uri class:
public static String getQueryString(String url, String tag) {
try {
Uri uri=Uri.parse(url);
return uri.getQueryParameter(tag);
}catch(Exception e){
Log.e(TAG,"getQueryString() " + e.getMessage());
}
return "";
}
and this is an example of how to use either of two methods:
String url = "http://www.jorgesys.com/advertisements/publicidadmobile.htm?position=x46&site=reform&awidth=800&aheight=120";
String tagValue = getQueryString(url,"awidth");
the value of tagValue is 800
For a servlet or a JSP page you can get querystring key/value pairs by using request.getParameter("paramname")
String name = request.getParameter("name");
There are other ways of doing it but that's the way I do it in all the servlets and jsp pages that I create.
On Android, I tried using #diyism answer but I encountered the space character issue raised by #rpetrich, for example:
I fill out a form where username = "us+us" and password = "pw pw" causing a URL string to look like:
http://somewhere?username=us%2Bus&password=pw+pw
However, #diyism code returns "us+us" and "pw+pw", i.e. it doesn't detect the space character. If the URL was rewritten with %20 the space character gets identified:
http://somewhere?username=us%2Bus&password=pw%20pw
This leads to the following fix:
Uri uri = Uri.parse(url_string.replace("+", "%20"));
uri.getQueryParameter("para1");
Parsing the query string is a bit more complicated than it seems, depending on how forgiving you want to be.
First, the query string is ascii bytes. You read in these bytes one at a time and convert them to characters. If the character is ? or & then it signals the start of a parameter name. If the character is = then it signals the start of a paramter value. If the character is % then it signals the start of an encoded byte. Here is where it gets tricky.
When you read in a % char you have to read the next two bytes and interpret them as hex digits. That means the next two bytes will be 0-9, a-f or A-F. Glue these two hex digits together to get your byte value. But remember, bytes are not characters. You have to know what encoding was used to encode the characters. The character é does not encode the same in UTF-8 as it does in ISO-8859-1. In general it's impossible to know what encoding was used for a given character set. I always use UTF-8 because my web site is configured to always serve everything using UTF-8 but in practice you can't be certain. Some user-agents will tell you the character encoding in the request; you can try to read that if you have a full HTTP request. If you just have a url in isolation, good luck.
Anyway, assuming you are using UTF-8 or some other multi-byte character encoding, now that you've decoded one encoded byte you have to set it aside until you capture the next byte. You need all the encoded bytes that are together because you can't url-decode properly one byte at a time. Set aside all the bytes that are together then decode them all at once to reconstruct your character.
Plus it gets more fun if you want to be lenient and account for user-agents that mangle urls. For example, some webmail clients double-encode things. Or double up the ?&= chars (for example: http://yoursite.com/blah??p1==v1&&p2==v2). If you want to try to gracefully deal with this, you will need to add more logic to your parser.
On Android its simple as the code below:
UrlQuerySanitizer sanitzer = new UrlQuerySanitizer(url);
String value = sanitzer.getValue("your_get_parameter");
Also if you don't want to register each expected query key use:
sanitzer.setAllowUnregisteredParamaters(true)
Before calling:
sanitzer.parseUrl(yourUrl)
On Android, you can use the Uri.parse static method of the android.net.Uri class to do the heavy lifting. If you're doing anything with URIs and Intents you'll want to use it anyways.
Just for reference, this is what I've ended up with (based on URLEncodedUtils, and returning a Map).
Features:
it accepts the query string part of the url (you can use request.getQueryString())
an empty query string will produce an empty Map
a parameter without a value (?test) will be mapped to an empty List<String>
Code:
public static Map<String, List<String>> getParameterMapOfLists(String queryString) {
Map<String, List<String>> mapOfLists = new HashMap<String, List<String>>();
if (queryString == null || queryString.length() == 0) {
return mapOfLists;
}
List<NameValuePair> list = URLEncodedUtils.parse(URI.create("http://localhost/?" + queryString), "UTF-8");
for (NameValuePair pair : list) {
List<String> values = mapOfLists.get(pair.getName());
if (values == null) {
values = new ArrayList<String>();
mapOfLists.put(pair.getName(), values);
}
if (pair.getValue() != null) {
values.add(pair.getValue());
}
}
return mapOfLists;
}
A compatibility helper (values are stored in a String array just as in ServletRequest.getParameterMap()):
public static Map<String, String[]> getParameterMap(String queryString) {
Map<String, List<String>> mapOfLists = getParameterMapOfLists(queryString);
Map<String, String[]> mapOfArrays = new HashMap<String, String[]>();
for (String key : mapOfLists.keySet()) {
mapOfArrays.put(key, mapOfLists.get(key).toArray(new String[] {}));
}
return mapOfArrays;
}
This works for me..
I'm not sure why every one was after a Map, List>
All I needed was a simple name value Map.
To keep things simple I used the build in URI.getQuery();
public static Map<String, String> getUrlParameters(URI uri)
throws UnsupportedEncodingException {
Map<String, String> params = new HashMap<String, String>();
for (String param : uri.getQuery().split("&")) {
String pair[] = param.split("=");
String key = URLDecoder.decode(pair[0], "UTF-8");
String value = "";
if (pair.length > 1) {
value = URLDecoder.decode(pair[1], "UTF-8");
}
params.put(new String(key), new String(value));
}
return params;
}
Guava's Multimap is better suited for this. Here is a short clean version:
Multimap<String, String> getUrlParameters(String url) {
try {
Multimap<String, String> ret = ArrayListMultimap.create();
for (NameValuePair param : URLEncodedUtils.parse(new URI(url), "UTF-8")) {
ret.put(param.getName(), param.getValue());
}
return ret;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
Origanally answered here
On Android, there is Uri class in package android.net . Note that Uri is part of android.net, while URI is part of java.net .
Uri class has many functions to extract query key-value pairs.
Following function returns key-value pairs in the form of HashMap.
In Java:
Map<String, String> getQueryKeyValueMap(Uri uri){
HashMap<String, String> keyValueMap = new HashMap();
String key;
String value;
Set<String> keyNamesList = uri.getQueryParameterNames();
Iterator iterator = keyNamesList.iterator();
while (iterator.hasNext()){
key = (String) iterator.next();
value = uri.getQueryParameter(key);
keyValueMap.put(key, value);
}
return keyValueMap;
}
In Kotlin:
fun getQueryKeyValueMap(uri: Uri): HashMap<String, String> {
val keyValueMap = HashMap<String, String>()
var key: String
var value: String
val keyNamesList = uri.queryParameterNames
val iterator = keyNamesList.iterator()
while (iterator.hasNext()) {
key = iterator.next() as String
value = uri.getQueryParameter(key) as String
keyValueMap.put(key, value)
}
return keyValueMap
}
Apache AXIS2 has a self-contained implementation of QueryStringParser.java. If you are not using Axis2, just download the sourcecode and test case from here -
http://svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/transport/http/util/QueryStringParser.java
http://svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/kernel/test/org/apache/axis2/transport/http/util/QueryStringParserTest.java
if (queryString != null)
{
final String[] arrParameters = queryString.split("&");
for (final String tempParameterString : arrParameters)
{
final String[] arrTempParameter = tempParameterString.split("=");
if (arrTempParameter.length >= 2)
{
final String parameterKey = arrTempParameter[0];
final String parameterValue = arrTempParameter[1];
//do something with the parameters
}
}
}
I don't think there is one in JRE. You can find similar functions in other packages like Apache HttpClient. If you don't use any other packages, you just have to write your own. It's not that hard. Here is what I use,
public class QueryString {
private Map<String, List<String>> parameters;
public QueryString(String qs) {
parameters = new TreeMap<String, List<String>>();
// Parse query string
String pairs[] = qs.split("&");
for (String pair : pairs) {
String name;
String value;
int pos = pair.indexOf('=');
// for "n=", the value is "", for "n", the value is null
if (pos == -1) {
name = pair;
value = null;
} else {
try {
name = URLDecoder.decode(pair.substring(0, pos), "UTF-8");
value = URLDecoder.decode(pair.substring(pos+1, pair.length()), "UTF-8");
} catch (UnsupportedEncodingException e) {
// Not really possible, throw unchecked
throw new IllegalStateException("No UTF-8");
}
}
List<String> list = parameters.get(name);
if (list == null) {
list = new ArrayList<String>();
parameters.put(name, list);
}
list.add(value);
}
}
public String getParameter(String name) {
List<String> values = parameters.get(name);
if (values == null)
return null;
if (values.size() == 0)
return "";
return values.get(0);
}
public String[] getParameterValues(String name) {
List<String> values = parameters.get(name);
if (values == null)
return null;
return (String[])values.toArray(new String[values.size()]);
}
public Enumeration<String> getParameterNames() {
return Collections.enumeration(parameters.keySet());
}
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = new TreeMap<String, String[]>();
for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
List<String> list = entry.getValue();
String[] values;
if (list == null)
values = null;
else
values = (String[]) list.toArray(new String[list.size()]);
map.put(entry.getKey(), values);
}
return map;
}
}
public static Map <String, String> parseQueryString (final URL url)
throws UnsupportedEncodingException
{
final Map <String, String> qps = new TreeMap <String, String> ();
final StringTokenizer pairs = new StringTokenizer (url.getQuery (), "&");
while (pairs.hasMoreTokens ())
{
final String pair = pairs.nextToken ();
final StringTokenizer parts = new StringTokenizer (pair, "=");
final String name = URLDecoder.decode (parts.nextToken (), "ISO-8859-1");
final String value = URLDecoder.decode (parts.nextToken (), "ISO-8859-1");
qps.put (name, value);
}
return qps;
}
Use Apache HttpComponents and wire it up with some collection code to access params by value: http://www.joelgerard.com/2012/09/14/parsing-query-strings-in-java-and-accessing-values-by-key/
using Guava:
Multimap<String,String> parseQueryString(String queryString, String encoding) {
LinkedListMultimap<String, String> result = LinkedListMultimap.create();
for(String entry : Splitter.on("&").omitEmptyStrings().split(queryString)) {
String pair [] = entry.split("=", 2);
try {
result.put(URLDecoder.decode(pair[0], encoding), pair.length == 2 ? URLDecoder.decode(pair[1], encoding) : null);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
return result;
}
Answering here because this is a popular thread. This is a clean solution in Kotlin that uses the recommended UrlQuerySanitizer api. See the official documentation. I have added a string builder to concatenate and display the params.
var myURL: String? = null
// if the url is sent from a different activity where you set it to a value
if (intent.hasExtra("my_value")) {
myURL = intent.extras.getString("my_value")
} else {
myURL = intent.dataString
}
val sanitizer = UrlQuerySanitizer(myURL)
// We don't want to manually define every expected query *key*, so we set this to true
sanitizer.allowUnregisteredParamaters = true
val parameterNamesToValues: List<UrlQuerySanitizer.ParameterValuePair> = sanitizer.parameterList
val parameterIterator: Iterator<UrlQuerySanitizer.ParameterValuePair> = parameterNamesToValues.iterator()
// Helper simply so we can display all values on screen
val stringBuilder = StringBuilder()
while (parameterIterator.hasNext()) {
val parameterValuePair: UrlQuerySanitizer.ParameterValuePair = parameterIterator.next()
val parameterName: String = parameterValuePair.mParameter
val parameterValue: String = parameterValuePair.mValue
// Append string to display all key value pairs
stringBuilder.append("Key: $parameterName\nValue: $parameterValue\n\n")
}
// Set a textView's text to display the string
val paramListString = stringBuilder.toString()
val textView: TextView = findViewById(R.id.activity_title) as TextView
textView.text = "Paramlist is \n\n$paramListString"
// to check if the url has specific keys
if (sanitizer.hasParameter("type")) {
val type = sanitizer.getValue("type")
println("sanitizer has type param $type")
}
this method takes the uri and return map of par name and par value
public static Map<String, String> getQueryMap(String uri) {
String queryParms[] = uri.split("\\?");
Map<String, String> map = new HashMap<>();//
if (queryParms == null || queryParms.length == 0) return map;
String[] params = queryParms[1].split("&");
for (String param : params) {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
return map;
}
You say "Java" but "not Java EE". Do you mean you are using JSP and/or servlets but not a full Java EE stack? If that's the case, then you should still have request.getParameter() available to you.
If you mean you are writing Java but you are not writing JSPs nor servlets, or that you're just using Java as your reference point but you're on some other platform that doesn't have built-in parameter parsing ... Wow, that just sounds like an unlikely question, but if so, the principle would be:
xparm=0
word=""
loop
get next char
if no char
exit loop
if char=='='
param_name[xparm]=word
word=""
else if char=='&'
param_value[xparm]=word
word=""
xparm=xparm+1
else if char=='%'
read next two chars
word=word+interpret the chars as hex digits to make a byte
else
word=word+char
(I could write Java code but that would be pointless, because if you have Java available, you can just use request.getParameters.)

Categories