Do you know of any utility class/library, that can convert Map into URL-friendly query string?
Example:
I have a map:
"param1"=12,
"param2"="cat"
I want to get:
param1=12¶m2=cat
final output
relativeUrl+param1=12¶m2=cat
The most robust one I saw off-the-shelf is the URLEncodedUtils class from Apache Http Compoments (HttpClient 4.0).
The method URLEncodedUtils.format() is what you need.
It doesn't use map so you can have duplicate parameter names, like,
a=1&a=2&b=3
Not that I recommend this kind of use of parameter names.
Here's something that I quickly wrote; I'm sure it can be improved upon.
import java.util.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class MapQuery {
static String urlEncodeUTF8(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
static String urlEncodeUTF8(Map<?,?> map) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<?,?> entry : map.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(String.format("%s=%s",
urlEncodeUTF8(entry.getKey().toString()),
urlEncodeUTF8(entry.getValue().toString())
));
}
return sb.toString();
}
public static void main(String[] args) {
Map<String,Object> map = new HashMap<String,Object>();
map.put("p1", 12);
map.put("p2", "cat");
map.put("p3", "a & b");
System.out.println(urlEncodeUTF8(map));
// prints "p3=a+%26+b&p2=cat&p1=12"
}
}
I found a smooth solution using java 8 and polygenelubricants' solution.
parameters.entrySet().stream()
.map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue()))
.reduce((p1, p2) -> p1 + "&" + p2)
.orElse("");
In Spring Util, there is a better way..,
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("key", key);
params.add("storeId", storeId);
params.add("orderId", orderId);
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("http://spsenthil.com/order").queryParams(params).build();
ListenableFuture<ResponseEntity<String>> responseFuture = restTemplate.getForEntity(uriComponents.toUriString(), String.class);
Update June 2016
Felt compelled to add an answer having seen far too many SOF answers with dated or inadequate answers to very common problem - a good library and some solid example usage for both parse and format operations.
Use org.apache.httpcomponents.httpclient library. The library contains this org.apache.http.client.utils.URLEncodedUtils class utility.
For example, it is easy to download this dependency from Maven:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
For my purposes I only needed to parse (read from query string to name-value pairs) and format (read from name-value pairs to query string) query strings. However, there are equivalents for doing the same with a URI (see commented out line below).
// Required imports
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// code snippet
public static void parseAndFormatExample() throws UnsupportedEncodingException {
final String queryString = "nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk";
System.out.println(queryString);
// => nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk
final List<NameValuePair> params =
URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8);
// List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), "UTF-8");
for (final NameValuePair param : params) {
System.out.println(param.getName() + " : " + param.getValue());
// => nonce : 12345
// => redirectCallbackUrl : http://www.bbc.co.uk
}
final String newQueryStringEncoded =
URLEncodedUtils.format(params, StandardCharsets.UTF_8);
// decode when printing to screen
final String newQueryStringDecoded =
URLDecoder.decode(newQueryStringEncoded, StandardCharsets.UTF_8.toString());
System.out.println(newQueryStringDecoded);
// => nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk
}
This library did exactly what I needed and was able to replace some hacked custom code.
If you actually want to build a complete URI, try URIBuilder from Apache Http Compoments (HttpClient 4).
This does not actually answer the question, but it answered the one I had when I found this question.
I wanted to build on #eclipse's answer using java 8 mapping and reducing.
protected String formatQueryParams(Map<String, String> params) {
return params.entrySet().stream()
.map(p -> p.getKey() + "=" + p.getValue())
.reduce((p1, p2) -> p1 + "&" + p2)
.map(s -> "?" + s)
.orElse("");
}
The extra map operation takes the reduced string and puts a ? in front only if the string exists.
Another 'one class'/no dependency way of doing it, handling single/multiple:
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class UrlQueryString {
private static final String DEFAULT_ENCODING = "UTF-8";
public static String buildQueryString(final LinkedHashMap<String, Object> map) {
try {
final Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
final StringBuilder sb = new StringBuilder(map.size() * 8);
while (it.hasNext()) {
final Map.Entry<String, Object> entry = it.next();
final String key = entry.getKey();
if (key != null) {
sb.append(URLEncoder.encode(key, DEFAULT_ENCODING));
sb.append('=');
final Object value = entry.getValue();
final String valueAsString = value != null ? URLEncoder.encode(value.toString(), DEFAULT_ENCODING) : "";
sb.append(valueAsString);
if (it.hasNext()) {
sb.append('&');
}
} else {
// Do what you want...for example:
assert false : String.format("Null key in query map: %s", map.entrySet());
}
}
return sb.toString();
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
public static String buildQueryStringMulti(final LinkedHashMap<String, List<Object>> map) {
try {
final StringBuilder sb = new StringBuilder(map.size() * 8);
for (final Iterator<Entry<String, List<Object>>> mapIterator = map.entrySet().iterator(); mapIterator.hasNext();) {
final Entry<String, List<Object>> entry = mapIterator.next();
final String key = entry.getKey();
if (key != null) {
final String keyEncoded = URLEncoder.encode(key, DEFAULT_ENCODING);
final List<Object> values = entry.getValue();
sb.append(keyEncoded);
sb.append('=');
if (values != null) {
for (final Iterator<Object> listIt = values.iterator(); listIt.hasNext();) {
final Object valueObject = listIt.next();
sb.append(valueObject != null ? URLEncoder.encode(valueObject.toString(), DEFAULT_ENCODING) : "");
if (listIt.hasNext()) {
sb.append('&');
sb.append(keyEncoded);
sb.append('=');
}
}
}
if (mapIterator.hasNext()) {
sb.append('&');
}
} else {
// Do what you want...for example:
assert false : String.format("Null key in query map: %s", map.entrySet());
}
}
return sb.toString();
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
public static void main(final String[] args) {
// Examples: could be turned into unit tests ...
{
final LinkedHashMap<String, Object> queryItems = new LinkedHashMap<String, Object>();
queryItems.put("brand", "C&A");
queryItems.put("count", null);
queryItems.put("misc", 42);
final String buildQueryString = buildQueryString(queryItems);
System.out.println(buildQueryString);
}
{
final LinkedHashMap<String, List<Object>> queryItems = new LinkedHashMap<String, List<Object>>();
queryItems.put("usernames", new ArrayList<Object>(Arrays.asList(new String[] { "bob", "john" })));
queryItems.put("nullValue", null);
queryItems.put("misc", new ArrayList<Object>(Arrays.asList(new Integer[] { 1, 2, 3 })));
final String buildQueryString = buildQueryStringMulti(queryItems);
System.out.println(buildQueryString);
}
}
}
You may use either simple (easier to write in most cases) or multiple when required. Note that both can be combined by adding an ampersand...
If you find any problems let me know in the comments.
This is the solution I implemented, using Java 8 and org.apache.http.client.URLEncodedUtils. It maps the entries of the map into a list of BasicNameValuePair and then uses Apache's URLEncodedUtils to turn that into a query string.
List<BasicNameValuePair> nameValuePairs = params.entrySet().stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
URLEncodedUtils.format(nameValuePairs, Charset.forName("UTF-8"));
There's nothing built into Java to do this. But, hey, Java is a programming language, so. Let's program it!
map
.entrySet()
.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining("&"))
This gives you param1=12¶m2=cat. Now we need to join the URL and this bit together. You'd think you can just do: URL + "?" + theAbove but if the URL already contains a question mark, you have to join it all together with "&" instead. One way to check is to see if there's a question mark in the URL someplace already.
Also, I don't quite know what is in your map. If it's raw stuff, you probably have to safeguard the call to e.getKey() and e.getValue() with URLEncoder.encode or similar.
Yet another way to go is that you take a wider view. Are you trying to append a map's content to a URL, or... Are you trying to make an HTTP (S) request from a Java process with the stuff in the map as (additional) HTTP params? In the latter case, you can look into an HTTP library like OkHttp which has some nice APIs to do this job, then you can forego any need to mess about with that URL in the first place.
Using EntrySet and Streams:
map
.entrySet()
.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining("&"));
You can use a Stream for this, but instead of appending query parameters myself I'd use a Uri.Builder. For example:
final Map<String, String> map = new HashMap<>();
map.put("param1", "cat");
map.put("param2", "12");
final Uri uri =
map.entrySet().stream().collect(
() -> Uri.parse("relativeUrl").buildUpon(),
(builder, e) -> builder.appendQueryParameter(e.getKey(), e.getValue()),
(b1, b2) -> { throw new UnsupportedOperationException(); }
).build();
//Or, if you consider it more readable...
final Uri.Builder builder = Uri.parse("relativeUrl").buildUpon();
map.entrySet().forEach(e -> builder.appendQueryParameter(e.getKey(), e.getValue())
final Uri uri = builder.build();
//...
assertEquals(Uri.parse("relativeUrl?param1=cat¶m2=12"), uri);
Here's a simple kotlin solution:
fun Map<String, String>.toUrlParams(): String =
entries.joinToString("&") {
it.key.toUrlEncoded() + "=" + it.value.toUrlEncoded()
}
fun String.toUrlEncoded(): String = URLEncoder.encode(
this, StandardCharsets.UTF_8
)
To improve a little bit upon #eclipse's answer: In Javaland a request parameter map is usually represented as a Map<String, String[]>, a Map<String, List<String>> or possibly some kind of MultiValueMap<String, String> which is sort of the same thing. In any case: a parameter can usually have multiple values. A Java 8 solution would therefore be something along these lines:
public String getQueryString(HttpServletRequest request, String encoding) {
Map<String, String[]> parameters = request.getParameterMap();
return parameters.entrySet().stream()
.flatMap(entry -> encodeMultiParameter(entry.getKey(), entry.getValue(), encoding))
.reduce((param1, param2) -> param1 + "&" + param2)
.orElse("");
}
private Stream<String> encodeMultiParameter(String key, String[] values, String encoding) {
return Stream.of(values).map(value -> encodeSingleParameter(key, value, encoding));
}
private String encodeSingleParameter(String key, String value, String encoding) {
return urlEncode(key, encoding) + "=" + urlEncode(value, encoding);
}
private String urlEncode(String value, String encoding) {
try {
return URLEncoder.encode(value, encoding);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Cannot url encode " + value, e);
}
}
If you need just the query string (not the whole URL) and you are using Spring Framework, you can do this:
import org.springframework.web.util.UriComponentsBuilder;
...
final String queryString = UriComponentsBuilder.newInstance()
.queryParam("many", "7", "14", "21")
.queryParam("single", "XYZ")
.build()
.toUri()
.getQuery();
System.out.println(queryString);
the result is:
many=7&many=14&many=21&single=XYZ
I make these functions than also send just the property name when the value is null.
public static String urlEncode(Map<?, ?> map) {
return map.entrySet().stream().map(
entry -> entry.getValue() == null
? urlEncode(entry.getKey())
: urlEncode(entry.getKey()) + "=" + urlEncode(entry.getValue())
).collect(Collectors.joining("&"));
}
public static String urlEncode(Object obj) {
return URLEncoder.encode(obj.toString(), StandardCharsets.UTF_8);
}
For multivalue map you can do like below (using java 8 stream api's)
Url encoding has been taken cared in this.
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
String urlQueryString = params.entrySet()
.stream()
.flatMap(stringListEntry -> stringListEntry.getValue()
.stream()
.map(s -> UriUtils.encode(stringListEntry.getKey(), StandardCharsets.UTF_8.toString()) + "=" +
UriUtils.encode(s, StandardCharsets.UTF_8.toString())))
.collect(Collectors.joining("&"));
Personally, I'd go for a solution like this, it's incredibly similar to the solution provided by #rzwitserloot, only subtle differences.
This solution is small, simple & clean, it requires very little in terms of dependencies, all of which are a part of the Java Util package.
Map<String, String> map = new HashMap<>();
map.put("param1", "12");
map.put("param2", "cat");
String output = "someUrl?";
output += map.entrySet()
.stream()
.map(x -> x.getKey() + "=" + x.getValue() + "&")
.collect(Collectors.joining("&"));
System.out.println(output.substring(0, output.length() -1));
Kotlin
mapOf(
"param1" to 12,
"param2" to "cat"
).map { "${it.key}=${it.value}" }
.joinToString("&")
a very lightweight answer it works for me
public static String queryStr(Map<String, String> data) throws UnsupportedEncodingException {
StringBuilder query = new StringBuilder();
for (Entry<String, String> entry : data.entrySet()) {
if (query.length() > 0) {
query.append('&');
}
query.append(entry.getKey()).append('=');
query.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
return query.toString();
}
Related
I've got the URI like this:
https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback
I need a collection with parsed elements:
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
To be exact, I need a Java equivalent for the C#/.NET HttpUtility.ParseQueryString method.
If you are looking for a way to achieve it without using an external library, the following code will help you.
public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
You can access the returned Map using <map>.get("client_id"), with the URL given in your question this would return "SS".
UPDATE URL-Decoding added
UPDATE As this answer is still quite popular, I made an improved version of the method above, which handles multiple parameters with the same key and parameters with no value as well.
public static Map<String, List<String>> splitQuery(URL url) throws UnsupportedEncodingException {
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
final String[] pairs = url.getQuery().split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new LinkedList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
query_pairs.get(key).add(value);
}
return query_pairs;
}
UPDATE Java8 version
public Map<String, List<String>> splitQuery(URL url) {
if (Strings.isNullOrEmpty(url.getQuery())) {
return Collections.emptyMap();
}
return Arrays.stream(url.getQuery().split("&"))
.map(this::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new, mapping(Map.Entry::getValue, toList())));
}
public SimpleImmutableEntry<String, String> splitQueryParameter(String it) {
final int idx = it.indexOf("=");
final String key = idx > 0 ? it.substring(0, idx) : it;
final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null;
return new SimpleImmutableEntry<>(
URLDecoder.decode(key, StandardCharsets.UTF_8),
URLDecoder.decode(value, StandardCharsets.UTF_8)
);
}
Running the above method with the URL
https://stackoverflow.com?param1=value1¶m2=¶m3=value3¶m3
returns this Map:
{param1=["value1"], param2=[null], param3=["value3", null]}
org.apache.http.client.utils.URLEncodedUtils
is a well known library that can do it for you
import org.apache.hc.client5.http.utils.URLEncodedUtils
String url = "http://www.example.com/something.html?one=1&two=2&three=3&three=3a";
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), Charset.forName("UTF-8"));
for (NameValuePair param : params) {
System.out.println(param.getName() + " : " + param.getValue());
}
Outputs
one : 1
two : 2
three : 3
three : 3a
If you are using Spring Framework:
public static void main(String[] args) {
String uri = "http://my.test.com/test?param1=ab¶m2=cd¶m2=ef";
MultiValueMap<String, String> parameters =
UriComponentsBuilder.fromUriString(uri).build().getQueryParams();
List<String> param1 = parameters.get("param1");
List<String> param2 = parameters.get("param2");
System.out.println("param1: " + param1.get(0));
System.out.println("param2: " + param2.get(0) + "," + param2.get(1));
}
You will get:
param1: ab
param2: cd,ef
use google Guava and do it in 2 lines:
import java.util.Map;
import com.google.common.base.Splitter;
public class Parser {
public static void main(String... args) {
String uri = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
String query = uri.split("\\?")[1];
final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(query);
System.out.println(map);
}
}
which gives you
{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
The shortest way I've found is this one:
MultiValueMap<String, String> queryParams =
UriComponentsBuilder.fromUriString(url).build().getQueryParams();
UPDATE: UriComponentsBuilder comes from Spring. Here the link.
For Android, if you are using OkHttp in your project. You might get a look at this. It simple and helpful.
final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
final String target = url.queryParameter("target");
final String id = url.queryParameter("id");
}
PLAIN Java 11
Given the URL to analyse:
URL url = new URL("https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback");
This solution collects a list of pairs:
List<Map.Entry<String, String>> list = Pattern.compile("&")
.splitAsStream(url.getQuery())
.map(s -> Arrays.copyOf(s.split("=", 2), 2))
.map(o -> Map.entry(decode(o[0]), decode(o[1])))
.collect(Collectors.toList());
This solution on the other hand collects a map (given that in a url there can be more parameters with same name but different values).
Map<String, List<String>> list = Pattern.compile("&")
.splitAsStream(url.getQuery())
.map(s -> Arrays.copyOf(s.split("=", 2), 2))
.collect(groupingBy(s -> decode(s[0]), mapping(s -> decode(s[1]), toList())));
Both the solutions must use an utility function to properly decode the parameters.
private static String decode(final String encoded) {
return Optional.ofNullable(encoded)
.map(e -> URLDecoder.decode(e, StandardCharsets.UTF_8))
.orElse(null);
}
On Android, there is a Uri class in package android.net . Note that Uri is part of android.net, whereas URI is part of java.net .
Uri class has many functions to extract key-value pairs from a query.
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
}
If you are using servlet doGet try this
request.getParameterMap()
Returns a java.util.Map of the parameters of this request.
Returns:
an immutable java.util.Map containing parameter names as keys and parameter values as map values. The keys in the parameter map are of type String. The values in the parameter map are of type String array.
(Java doc)
Netty also provides a nice query string parser called QueryStringDecoder.
In one line of code, it can parse the URL in the question.
I like because it doesn't require catching or throwing java.net.MalformedURLException.
In one line:
Map<String, List<String>> parameters = new QueryStringDecoder(url).parameters();
See javadocs here: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html
Here is a short, self contained, correct example:
import io.netty.handler.codec.http.QueryStringDecoder;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
public class UrlParse {
public static void main(String... args) {
String url = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
QueryStringDecoder decoder = new QueryStringDecoder(url);
Map<String, List<String>> parameters = decoder.parameters();
print(parameters);
}
private static void print(final Map<String, List<String>> parameters) {
System.out.println("NAME VALUE");
System.out.println("------------------------");
parameters.forEach((key, values) ->
values.forEach(val ->
System.out.println(StringUtils.rightPad(key, 19) + val)));
}
}
which generates
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
If you're using Java 8 and you're willing to write a few reusable methods, you can do it in one line.
private Map<String, List<String>> parse(final String query) {
return Arrays.asList(query.split("&")).stream().map(p -> p.split("=")).collect(Collectors.toMap(s -> decode(index(s, 0)), s -> Arrays.asList(decode(index(s, 1))), this::mergeLists));
}
private <T> List<T> mergeLists(final List<T> l1, final List<T> l2) {
List<T> list = new ArrayList<>();
list.addAll(l1);
list.addAll(l2);
return list;
}
private static <T> T index(final T[] array, final int index) {
return index >= array.length ? null : array[index];
}
private static String decode(final String encoded) {
try {
return encoded == null ? null : URLDecoder.decode(encoded, "UTF-8");
} catch(final UnsupportedEncodingException e) {
throw new RuntimeException("Impossible: UTF-8 is a required encoding", e);
}
}
But that's a pretty brutal line.
There a new version of Apache HTTP client - org.apache.httpcomponents.client5 - where URLEncodedUtils is now deprecated. URIBuilder should be used instead:
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.net.URIBuilder;
private static Map<String, String> getQueryParameters(final String url) throws URISyntaxException {
return new URIBuilder(new URI(url), StandardCharsets.UTF_8).getQueryParams()
.stream()
.collect(Collectors.toMap(NameValuePair::getName,
nameValuePair -> URLDecoder.decode(nameValuePair.getValue(), StandardCharsets.UTF_8)));
}
A ready-to-use solution for decoding of URI query part (incl. decoding and multi parameter values)
Comments
I wasn't happy with the code provided by #Pr0gr4mm3r in https://stackoverflow.com/a/13592567/1211082 . The Stream-based solution does not do URLDecoding, the mutable version clumpsy.
Thus I elaborated a solution that
Can decompose a URI query part into a Map<String, List<Optional<String>>>
Can handle multiple values for the same parameter name
Can represent parameters without a value properly (Optional.empty() instead of null)
Decodes parameter names and values correctly via URLdecode
Is based on Java 8 Streams
Is directly usable (see code including imports below)
Allows for proper error handling (here via turning a checked exception UnsupportedEncodingExceptioninto a runtime exception RuntimeUnsupportedEncodingException that allows interplay with stream. (Wrapping regular function into functions throwing checked exceptions is a pain. And Scala Try is not available in the Java language default.)
Java Code
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import static java.util.stream.Collectors.*;
public class URIParameterDecode {
/**
* Decode parameters in query part of a URI into a map from parameter name to its parameter values.
* For parameters that occur multiple times each value is collected.
* Proper decoding of the parameters is performed.
*
* Example
* <pre>a=1&b=2&c=&a=4</pre>
* is converted into
* <pre>{a=[Optional[1], Optional[4]], b=[Optional[2]], c=[Optional.empty]}</pre>
* #param query the query part of an URI
* #return map of parameters names into a list of their values.
*
*/
public static Map<String, List<Optional<String>>> splitQuery(String query) {
if (query == null || query.isEmpty()) {
return Collections.emptyMap();
}
return Arrays.stream(query.split("&"))
.map(p -> splitQueryParameter(p))
.collect(groupingBy(e -> e.get0(), // group by parameter name
mapping(e -> e.get1(), toList())));// keep parameter values and assemble into list
}
public static Pair<String, Optional<String>> splitQueryParameter(String parameter) {
final String enc = "UTF-8";
List<String> keyValue = Arrays.stream(parameter.split("="))
.map(e -> {
try {
return URLDecoder.decode(e, enc);
} catch (UnsupportedEncodingException ex) {
throw new RuntimeUnsupportedEncodingException(ex);
}
}).collect(toList());
if (keyValue.size() == 2) {
return new Pair(keyValue.get(0), Optional.of(keyValue.get(1)));
} else {
return new Pair(keyValue.get(0), Optional.empty());
}
}
/** Runtime exception (instead of checked exception) to denote unsupported enconding */
public static class RuntimeUnsupportedEncodingException extends RuntimeException {
public RuntimeUnsupportedEncodingException(Throwable cause) {
super(cause);
}
}
/**
* A simple pair of two elements
* #param <U> first element
* #param <V> second element
*/
public static class Pair<U, V> {
U a;
V b;
public Pair(U u, V v) {
this.a = u;
this.b = v;
}
public U get0() {
return a;
}
public V get1() {
return b;
}
}
}
Scala Code
... and for the sake of completeness I can not resist to provide the solution in Scala that dominates by brevity and beauty
import java.net.URLDecoder
object Decode {
def main(args: Array[String]): Unit = {
val input = "a=1&b=2&c=&a=4";
println(separate(input))
}
def separate(input: String) : Map[String, List[Option[String]]] = {
case class Parameter(key: String, value: Option[String])
def separateParameter(parameter: String) : Parameter =
parameter.split("=")
.map(e => URLDecoder.decode(e, "UTF-8")) match {
case Array(key, value) => Parameter(key, Some(value))
case Array(key) => Parameter(key, None)
}
input.split("&").toList
.map(p => separateParameter(p))
.groupBy(p => p.key)
.mapValues(vs => vs.map(p => p.value))
}
}
Using above mentioned comments and solutions, I am storing all the query parameters using Map<String, Object> where Objects either can be string or Set<String>. The solution is given below. It is recommended to use some kind of url validator to validate the url first and then call convertQueryStringToMap method.
private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
public static Map<String, Object> convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException {
List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME);
Map<String, Object> queryStringMap = new HashMap<>();
for(NameValuePair param : params){
queryStringMap.put(param.getName(), handleMultiValuedQueryParam(queryStringMap, param.getName(), param.getValue()));
}
return queryStringMap;
}
private static Object handleMultiValuedQueryParam(Map responseMap, String key, String value) {
if (!responseMap.containsKey(key)) {
return value.contains(",") ? new HashSet<String>(Arrays.asList(value.split(","))) : value;
} else {
Set<String> queryValueSet = responseMap.get(key) instanceof Set ? (Set<String>) responseMap.get(key) : new HashSet<String>();
if (value.contains(",")) {
queryValueSet.addAll(Arrays.asList(value.split(",")));
} else {
queryValueSet.add(value);
}
return queryValueSet;
}
}
I had a go at a Kotlin version seeing how this is the top result in Google.
#Throws(UnsupportedEncodingException::class)
fun splitQuery(url: URL): Map<String, List<String>> {
val queryPairs = LinkedHashMap<String, ArrayList<String>>()
url.query.split("&".toRegex())
.dropLastWhile { it.isEmpty() }
.map { it.split('=') }
.map { it.getOrEmpty(0).decodeToUTF8() to it.getOrEmpty(1).decodeToUTF8() }
.forEach { (key, value) ->
if (!queryPairs.containsKey(key)) {
queryPairs[key] = arrayListOf(value)
} else {
if(!queryPairs[key]!!.contains(value)) {
queryPairs[key]!!.add(value)
}
}
}
return queryPairs
}
And the extension methods
fun List<String>.getOrEmpty(index: Int) : String {
return getOrElse(index) {""}
}
fun String.decodeToUTF8(): String {
URLDecoder.decode(this, "UTF-8")
}
Also, I would recommend regex based implementation of URLParser
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class URLParser {
private final String query;
public URLParser(String query) {
this.query = query;
}
public String get(String name) {
String regex = "(?:^|\\?|&)" + name + "=(.*?)(?:&|$)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(this.query);
if (matcher.find()) {
return matcher.group(1);
}
return "";
}
}
This class is easy to use. It just needs the URL or the query string on initialization and parses value by given key.
class Main {
public static void main(String[] args) {
URLParser parser = new URLParser("https://www.google.com/search?q=java+parse+url+params&oq=java+parse+url+params&aqs=chrome..69i57j0i10.18908j0j7&sourceid=chrome&ie=UTF-8");
System.out.println(parser.get("q")); // java+parse+url+params
System.out.println(parser.get("sourceid")); // chrome
System.out.println(parser.get("ie")); // UTF-8
}
}
Kotlin's Answer with initial reference from https://stackoverflow.com/a/51024552/3286489, but with improved version by tidying up codes and provides 2 versions of it, and use immutable collection operations
Use java.net.URI to extract the Query. Then use the below provided extension functions
Assuming you only want the last value of query i.e. page2&page3 will get {page=3}, use the below extension function
fun URI.getQueryMap(): Map<String, String> {
if (query == null) return emptyMap()
return query.split("&")
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.associateBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
Assuming you want a list of all value for the query i.e. page2&page3 will get {page=[2, 3]}
fun URI.getQueryMapList(): Map<String, List<String>> {
if (query == null) return emptyMap()
return query.split("&")
.distinct()
.mapNotNull { element -> element.split("=")
.takeIf { it.size == 2 && it.none { it.isBlank() } } }
.groupBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
}
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
The way to use it as below
val uri = URI("schema://host/path/?page=&page=2&page=2&page=3")
println(uri.getQueryMapList()) // Result is {page=[2, 3]}
println(uri.getQueryMap()) // Result is {page=3}
There are plenty of answers which work for your query as you've indicated when it has single parameter definitions. In some applications it may be useful to handle a few extra query parameter edge cases such as:
list of parameter values such as param1¶m1=value¶m1= meaning param1 is set to List.of("", "value", "")
invalid permutations such as querypath?&=&&=noparamname&.
use empty string not null in maps a= means "a" is List.of("") to match web servlet handling
This uses a Stream with filters and groupingBy to collect to Map<String, List<String>>:
public static Map<String, List<String>> getParameterValues(URL url) {
return Arrays.stream(url.getQuery().split("&"))
.map(s -> s.split("="))
// filter out empty parameter names (as in Tomcat) "?&=&&=value&":
.filter(arr -> arr.length > 0 && arr[0].length() > 0)
.collect(Collectors.groupingBy(arr -> URLDecoder.decode(arr[0], StandardCharsets.UTF_8),
// drop this line for not-name definition order Map:
LinkedHashMap::new,
Collectors.mapping(arr -> arr.length < 2 ? "" : URLDecoder.decode(arr[1], StandardCharsets.UTF_8), Collectors.toList())));
}
If you are using Spring, add an argument of type #RequestParam Map<String,String> to your controller method, and Spring will construct the map for you!
Just an update to the Java 8 version
public Map<String, List<String>> splitQuery(URL url) {
if (Strings.isNullOrEmpty(url.getQuery())) {
return Collections.emptyMap();
}
return Arrays.stream(url.getQuery().split("&"))
.map(this::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new, **Collectors**.mapping(Map.Entry::getValue, **Collectors**.toList())));
}
mapping and toList() methods have to be used with Collectors which was not mentioned in the top answer. Otherwise it would throw compilation error in IDE
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 (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")
}
Here is my solution with reduce and Optional:
private Optional<SimpleImmutableEntry<String, String>> splitKeyValue(String text) {
String[] v = text.split("=");
if (v.length == 1 || v.length == 2) {
String key = URLDecoder.decode(v[0], StandardCharsets.UTF_8);
String value = v.length == 2 ? URLDecoder.decode(v[1], StandardCharsets.UTF_8) : null;
return Optional.of(new SimpleImmutableEntry<String, String>(key, value));
} else
return Optional.empty();
}
private HashMap<String, String> parseQuery(URI uri) {
HashMap<String, String> params = Arrays.stream(uri.getQuery()
.split("&"))
.map(this::splitKeyValue)
.filter(Optional::isPresent)
.map(Optional::get)
.reduce(
// initial value
new HashMap<String, String>(),
// accumulator
(map, kv) -> {
map.put(kv.getKey(), kv.getValue());
return map;
},
// combiner
(a, b) -> {
a.putAll(b);
return a;
});
return params;
}
I ignore duplicate parameters (I take the last one).
I use Optional<SimpleImmutableEntry<String, String>> to ignore garbage later
The reduction start with an empty map, then populate it on each SimpleImmutableEntry
In case you ask, reduce requires this weird combiner in the last parameter, which is only used in parallel streams. Its goal is to merge two intermediate results (here HashMap).
If you happen to have cxf-core on the classpath and you know you have no repeated query params, you may want to use UrlUtils.parseQueryString.
The Eclipse Jersey REST framework supports this through UriComponent. Example:
import org.glassfish.jersey.uri.UriComponent;
String uri = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
MultivaluedMap<String, String> params = UriComponent.decodeQuery(URI.create(uri), true);
for (String key : params.keySet()) {
System.out.println(key + ": " + params.getFirst(key));
}
If just want the parameters after the URL from a String. Then the following code will work. I am just assuming the simple Url. I mean no hard and fast checking and decoding. Like in one of my test case I got the Url and I know I just need the value of the paramaters. The url was simple. No encoding decoding needed.
String location = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
String location1 = "https://stackoverflow.com?param1=value1¶m2=value2¶m3=value3";
String location2 = "https://stackoverflow.com?param1=value1¶m2=¶m3=value3¶m3";
Map<String, String> paramsMap = Stream.of(location)
.filter(l -> l.indexOf("?") != -1)
.map(l -> l.substring(l.indexOf("?") + 1, l.length()))
.flatMap(q -> Pattern.compile("&").splitAsStream(q))
.map(s -> s.split("="))
.filter(a -> a.length == 2)
.collect(Collectors.toMap(
a -> a[0],
a -> a[1],
(existing, replacement) -> existing + ", " + replacement,
LinkedHashMap::new
));
System.out.println(paramsMap);
Thanks
That seems tidy to me the best way:
static Map<String, String> decomposeQueryString(String query, Charset charset) {
return Arrays.stream(query.split("&"))
.map(pair -> pair.split("=", 2))
.collect(Collectors.toMap(
pair -> URLDecoder.decode(pair[0], charset),
pair -> pair.length > 1 ? URLDecoder.decode(pair[1], charset) : null)
);
}
The prerequisite is that your query syntax does not allow repeated parameters.
The Hutool framework supports this through HttpUtil. Example:
import cn.hutool.http.HttpUtil;
String url ="https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
Map<String, List<String>> stringListMap = HttpUtil.decodeParams(url, "UTF-8");
System.out.println("decodeParams:" + stringListMap);
You will get:
decodeParams:{client_id=[SS], response_type=[code], scope=[N_FULL], access_type=[offline], redirect_uri=[http://localhost/Callback]}
A kotlin version
of the answer Answer by matthias provided
fun decomposeQueryString(query: String, charset: Charset): Map<String, String?> {
return if (query.split("?").size <= 1)
emptyMap()
else {
query.split("?")[1]
.split("&")
.map { it.split(Pattern.compile("="), 2) }
.associate {
Pair(
URLDecoder.decode(it[0], charset.name()),
if (it.size > 1) URLDecoder.decode(it[1], charset.name()) else null
)
}
}
}
This takes of the first parameter after the question mark '?' as well.
Plain Java, No Special Libraries, Nothing Fancy
// assumes you are parsing a line that looks like:
// /path/resource?key=value¶meter=value
// which you got from a request header line that looks like:
// GET /path/resource?key=value¶meter=value HTTP/1.1
public HashMap<String, String> parseQuery(String path){
if(path == null || path.isEmpty()){ //basic sanity check
return null;
}
int indexOfQ = path.indexOf("?"); //where the query string starts
if(indexOfQ == -1){return null;} //check query exists
String queryString = path.substring(indexOfQ + 1);
String[] queryStringArray = queryString.split("&");
Map<String, String> kvMap = new HashMap<>();
for(String kvString : queryStringArray){
int indexOfE = kvString.indexOf("="); //check query is formed correctly
if(indexOfE == -1 || indexOfE == 0){return null;}
String[] kvPairArray = kvString.split("=");
kvMap.put(kvPairArray[0], kvPairArray[1]);
}
return kvMap;
}
org.keycloak.common.util.UriUtils
I had to parse URIs and Query Parameters in a Keycloak extension and found this utility classes very useful:
org.keycloak.common.util.UriUtils:
static MultivaluedHashMap<String,String> decodeQueryString(String queryString)
There is also a useful method to delete one query parameter:
static String stripQueryParam(String url, String name)
And to parse the URL there is
org.keycloak.common.util.KeycloakUriBuilder:
KeycloakUriBuilder uri(String uriTemplate)
String getQuery()
and lots of other goodies.
I have a following String:
utm_campaign=some_campaign&utm_content=video&utm_medium=paidsocial&utm_source=facebook"
I want to split it into keys/values and put it into HashMap:
KEY: utm_campaign VALUE: some_campaign
...
etc
Also, before putting it into Map, i should replace utm with ga in this method:
private String convertUtmToGa(String utm) {
switch (utm) {
case "utm_medium":
return "ga:medium";
case "utm_campaign":
return "ga:campaign";
case "utm_source":
return "ga:source";
case "utm_content":
return "ga:adContent";
default:
return utm;
}
}
So I should get Map like this:
ga:campaign=some_campaign, ga:adContent=video, ga:medium=paidsocial
I tried with:
private Map<String, String> extractUtmMarks(String paramsString) {
if (paramsString != null) {
Map<String, String> params = new HashMap<>();
String[] paramsAndValues = paramsString.split("&");
for (String paramAndValue : paramsAndValues) {
String[] keyValueArray = paramAndValue.split("=");
if (keyValueArray.length == 2 && !keyValueArray[1].isBlank() && !isUtmMarkContainsMacro(keyValueArray[1])) {
String param = convertUtmMarkToGa(keyValueArray[0]);
if (!param.isBlank()) {
params.put(param, keyValueArray[1]);
}
}
}
return params;
}
return Collections.emptyMap();
}
But this code looks not good. Can i get a better solution?
Using Streams...
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
...
final String input = "utm_campaign=some_campaign&utm_content=video&utm_medium=paidsocial&utm_source=facebook";
final Map<String, String> map =
Arrays.stream(input.split("&")) // Stream of "K=V" strings
.map(subString -> subString.split("=")) // Returns 2 element array -- {K,V}
.collect(Collectors.toMap(tokens -> convertUtmToGa(tokens[0]), tokens -> tokens[1]));
You can also use streams but if your methods works, why change it?
first split on the &
then on the =
String v =
"utm_campaign=some_campaign&utm_content=video&utm_medium=paidsocial&utm_source=facebook";
Map<String, String> map = Arrays.stream(v.split("&"))
.map(arr -> arr.split("="))
.collect(Collectors.toMap(a -> a[0], a -> a[1]));
map.entrySet().forEach(System.out::println);
Prints
utm_campaign=some_campaign
utm_medium=paidsocial
utm_content=video
utm_source=facebook
I have a URL and I need to get the value of v from this URL.
Here is my URL: http://www.youtube.com/watch?v=_RCIP6OrQrE
How can I do that?
I think the one of the easiest ways out would be to parse the string returned by URL.getQuery() as
public static Map<String, String> getQueryMap(String query) {
String[] params = query.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);
}
return map;
}
You can use the map returned by this function to retrieve the value keying in the parameter name.
If you're on Android, you can do this:
Uri uri = Uri.parse(url);
String v = uri.getQueryParameter("v");
I have something like this:
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;
private String getParamValue(String link, String paramName) throws URISyntaxException {
List<NameValuePair> queryParams = new URIBuilder(link).getQueryParams();
return queryParams.stream()
.filter(param -> param.getName().equalsIgnoreCase(paramName))
.map(NameValuePair::getValue)
.findFirst()
.orElse("");
}
I wrote this last month for Joomla Module when implementing youtube videos (with the Gdata API). I've since converted it to java.
Import These Libraries
import java.net.URL;
import java.util.regex.*;
Copy/Paste this function
public String getVideoId( String videoId ) throws Exception {
String pattern = "^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(videoId);
int youtu = videoId.indexOf("youtu");
if(m.matches() && youtu != -1){
int ytu = videoId.indexOf("http://youtu.be/");
if(ytu != -1) {
String[] split = videoId.split(".be/");
return split[1];
}
URL youtube = new URL(videoId);
String[] split = youtube.getQuery().split("=");
int query = split[1].indexOf("&");
if(query != -1){
String[] nSplit = split[1].split("&");
return nSplit[0];
} else return split[1];
}
return null; //throw something or return what you want
}
URL's it will work with
http://www.youtube.com/watch?v=k0BWlvnBmIE (General URL)
http://youtu.be/k0BWlvnBmIE (Share URL)
http://www.youtube.com/watch?v=UWb5Qc-fBvk&list=FLzH5IF4Lwgv-DM3CupM3Zog&index=2 (Playlist URL)
Import these libraries
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
Similar to the verisimilitude, but with the capabilities of handling multivalue parameters. Note: I've seen HTTP GET requests without a value, in this case the value will be null.
public static List<NameValuePair> getQueryMap(String query)
{
List<NameValuePair> queryMap = new ArrayList<NameValuePair>();
String[] params = query.split(Pattern.quote("&"));
for (String param : params)
{
String[] chunks = param.split(Pattern.quote("="));
String name = chunks[0], value = null;
if(chunks.length > 1) {
value = chunks[1];
}
queryMap.add(new BasicNameValuePair(name, value));
}
return queryMap;
}
Example:
GET /bottom.gif?e235c08=1509896923&%49%6E%...
Using pure Java 8
Assumming you want to extract param "v" from url:
String paramV = Stream.of(url.split("?")[1].split("&"))
.map(kv -> kv.split("="))
.filter(kv -> "v".equalsIgnoreCase(kv[0]))
.map(kv -> kv[1])
.findFirst()
.orElse("");
Assuming the URL syntax will always be http://www.youtube.com/watch?v= ...
String v = "http://www.youtube.com/watch?v=_RCIP6OrQrE".substring(31);
or disregarding the prefix syntax:
String url = "http://www.youtube.com/watch?v=_RCIP6OrQrE";
String v = url.substring(url.indexOf("v=") + 2);
I believe we have a better approach to answer this question.
1: Define a function that returns Map values.
Here we go.
public Map<String, String> getUrlValues(String url) throws UnsupportedEncodingException {
int i = url.indexOf("?");
Map<String, String> paramsMap = new HashMap<>();
if (i > -1) {
String searchURL = url.substring(url.indexOf("?") + 1);
String params[] = searchURL.split("&");
for (String param : params) {
String temp[] = param.split("=");
paramsMap.put(temp[0], java.net.URLDecoder.decode(temp[1], "UTF-8"));
}
}
return paramsMap;
}
2: Call your function surrounding with a try catch block
Here we go
try {
Map<String, String> values = getUrlValues("https://example.com/index.php?form_id=9&page=1&view_id=78");
String formId = values.get("form_id");
String page = values.get("page");
String viewId = values.get("view_id");
Log.d("FormID", formId);
Log.d("Page", page);
Log.d("ViewID", viewId);
} catch (UnsupportedEncodingException e) {
Log.e("Error", e.getMessage());
}
If you are using Jersey (which I was, my server component needs to make outbound HTTP requests) it contains the following public method:
var multiValueMap = UriComponent.decodeQuery(uri, true);
It is part of org.glassfish.jersey.uri.UriComponent, and the javadoc is here. Whilst you may not want all of Jersey, it is part of the Jersey common package which isn't too bad on dependencies...
I solved the problem like this
public static String getUrlParameterValue(String url, String paramName) {
String value = "";
List<NameValuePair> result = null;
try {
result = URLEncodedUtils.parse(new URI(url), UTF_8);
value = result.stream().filter(pair -> pair.getName().equals(paramName)).findFirst().get().getValue();
System.out.println("--------------> \n" + paramName + " : " + value + "\n");
} catch (URISyntaxException e) {
e.printStackTrace();
}
return value;
}
this will work for all sort of youtube url :
if url could be
youtube.com/?v=_RCIP6OrQrE
youtube.com/v/_RCIP6OrQrE
youtube.com/watch?v=_RCIP6OrQrE
youtube.com/watch?v=_RCIP6OrQrE&feature=whatever&this=that
Pattern p = Pattern.compile("http.*\\?v=([a-zA-Z0-9_\\-]+)(?:&.)*");
String url = "http://www.youtube.com/watch?v=_RCIP6OrQrE";
Matcher m = p.matcher(url.trim()); //trim to remove leading and trailing space if any
if (m.matches()) {
url = m.group(1);
}
System.out.println(url);
this will extract video id from your url
further reference
My solution mayble not good
String url = "https://www.youtube.com/watch?param=test&v=XcHJMiSy_1c&lis=test";
int start = url.indexOf("v=")+2;
// int start = url.indexOf("list=")+5; **5 is length of ("list=")**
int end = url.indexOf("&", start);
end = (end == -1 ? url.length() : end);
System.out.println(url.substring(start, end));
// result: XcHJMiSy_1c
work fine with:
https://www.youtube.com/watch?param=test&v=XcHJMiSy_1c&lis=test
https://www.youtube.com/watch?v=XcHJMiSy_1c
public static String getQueryMap(String query) {
String[] params = query.split("&");
for (String param : params) {
String name = param.split("=")[0];
if ("YourParam".equals(name)) {
return param.split("=")[1];
}
}
return null;
}
Does anyone have, or know of, a java class that I can use to manipulate query strings?
Essentially I'd like a class that I can simply give a query string to and then delete, add and modify query string KVP's.
Thanks in advance.
EDIT
In response to a comment made to this question, the query string will look something like this;
N=123+456+112&Ntt=koala&D=abc
So I'd like to pass this class the query string and say something like;
String[] N = queryStringClass.getParameter("N");
and then maybe
queryStringClass.setParameter("N", N);
and maybe queryStringClass.removeParameter("N");
Or something to that effect.
SOmething like this
public static Map<String, String> getQueryMap(String query)
{
String[] params = query.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);
}
return map;
}
To iterate the map simply:
String query = url.getQuery();
Map<String, String> map = getQueryMap(query);
Set<String> keys = map.keySet();
for (String key : keys)
{
System.out.println("Name=" + key);
System.out.println("Value=" + map.get(key));
}
You can also use Google Guava's Splitter.
String queryString = "variableA=89&variableB=100";
Map<String,String> queryParameters = Splitter
.on("&")
.withKeyValueSeparator("=")
.split(queryString);
System.out.println(queryParameters.get("variableA"));
prints out
89
This I think is a very readable alternative to parsing it yourself.
Edit: As #raulk pointed out, this solution does not account for escaped characters. However, this may not be an issue because before you URL-Decode, the query string is guaranteed to not have any escaped characters that conflict with '=' and '&'. You can use this to your advantage in the following way.
Say that you must decode the following query string:
a=%26%23%25!)%23(%40!&b=%23%24(%40)%24%40%40))%24%23%5E*%26
which is URL encoded, then you are guaranteed that the '&' and '=' are specifically used for separating pairs and key from value, respectively, at which point you can use the Guava splitter to get:
a = %26%23%25!)%23(%40!
b = %23%24(%40)%24%40%40))%24%23%5E*%26
Once you have obtained the key-value pairs, then you can URL decode them separately.
a = &#%!)#(#!
b = #$(#)$##))$#^*&
That should cover all cases.
If you are using J2EE, you can use ServletRequest.getParameterValues().
Otherwise, I don't think Java has any common classes for query string handling. Writing your own shouldn't be too hard, though there are certain tricky edge cases, such as realizing that technically the same key may appear more than once in the query string.
One implementation might look like:
import java.util.*;
import java.net.URLEncoder;
import java.net.URLDecoder;
public class QueryParams {
private static class KVP {
final String key;
final String value;
KVP (String key, String value) {
this.key = key;
this.value = value;
}
}
List<KVP> query = new ArrayList<KVP>();
public QueryParams(String queryString) {
parse(queryString);
}
public QueryParams() {
}
public void addParam(String key, String value) {
if (key == null || value == null)
throw new NullPointerException("null parameter key or value");
query.add(new KVP(key, value));
}
private void parse(String queryString) {
for (String pair : queryString.split("&")) {
int eq = pair.indexOf("=");
if (eq < 0) {
// key with no value
addParam(URLDecoder.decode(pair), "");
} else {
// key=value
String key = URLDecoder.decode(pair.substring(0, eq));
String value = URLDecoder.decode(pair.substring(eq + 1));
query.add(new KVP(key, value));
}
}
}
public String toQueryString() {
StringBuilder sb = new StringBuilder();
for (KVP kvp : query) {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(URLEncoder.encode(kvp.key));
if (!kvp.value.equals("")) {
sb.append('=');
sb.append(URLEncoder.encode(kvp.value));
}
}
return sb.toString();
}
public String getParameter(String key) {
for (KVP kvp : query) {
if (kvp.key.equals(key)) {
return kvp.value;
}
}
return null;
}
public List<String> getParameterValues(String key) {
List<String> list = new LinkedList<String>();
for (KVP kvp : query) {
if (kvp.key.equals(key)) {
list.add(kvp.value);
}
}
return list;
}
public static void main(String[] args) {
QueryParams qp = new QueryParams("k1=v1&k2&k3=v3&k1=v4&k1&k5=hello+%22world");
System.out.println("getParameter:");
String[] keys = new String[] { "k1", "k2", "k3", "k5" };
for (String key : keys) {
System.out.println(key + ": " + qp.getParameter(key));
}
System.out.println("getParameters(k1): " + qp.getParameterValues("k1"));
}
}
Another way is to use apache http-components. It's a bit hacky, but at least you leverage all the parsing corner cases:
List<NameValuePair> params =
URLEncodedUtils.parse("http://example.com/?" + queryString, Charset.forName("UTF-8"));
That'll give you a List of NameValuePair objects that should be easy to work with.
You can create a util method and use regular expression to parse it. A pattern like "[;&]" should suffice.
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.)