How to convert map to url query string?

JavaJakarta EeUtilities

Java Problem Overview


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&param2=cat

final output

relativeUrl+param1=12&param2=cat

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

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"
	}
}

Solution 3 - Java

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("");

Solution 4 - Java

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);

Solution 5 - Java

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.

Solution 6 - Java

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.

Solution 7 - Java

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.

Solution 8 - Java

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.

Solution 9 - Java

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"));

Solution 10 - Java

Using EntrySet and Streams:

map
  .entrySet()
  .stream()
  .map(e -> e.getKey() + "=" + e.getValue())
  .collect(Collectors.joining("&"));

Solution 11 - Java

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.

Solution 12 - Java

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&param2=12"), uri);

Solution 13 - Java

I think this is better for memory usage and performance, and I want to send just the property name when the value is null.

public static String toUrlEncode(Map<String, Object> map) {
    StringBuilder sb = new StringBuilder();
    map.entrySet().stream()
            .forEach(entry
                    -> (entry.getValue() == null
                    ? sb.append(entry.getKey())
                    : sb.append(entry.getKey())
                            .append('=')
                            .append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8)))
                    .append('&')
            );
    sb.delete(sb.length() - 1, sb.length());
    return sb.toString();
}

Solution 14 - Java

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);
    }
}

Solution 15 - Java

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
)

Solution 16 - Java

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));

Solution 17 - Java

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("&"));

Solution 18 - Java

Kotlin

mapOf(
  "param1" to 12,
  "param2" to "cat"
).map { "${it.key}=${it.value}" }
  .joinToString("&")

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUla KrukarView Question on Stackoverflow
Solution 1 - JavaZZ CoderView Answer on Stackoverflow
Solution 2 - JavapolygenelubricantsView Answer on Stackoverflow
Solution 3 - JavaeclipseView Answer on Stackoverflow
Solution 4 - JavaSenthil Arumugam SPView Answer on Stackoverflow
Solution 5 - JavaarcseldonView Answer on Stackoverflow
Solution 6 - JavasbzoomView Answer on Stackoverflow
Solution 7 - JavaThraidhView Answer on Stackoverflow
Solution 8 - JavaChristophe RoussyView Answer on Stackoverflow
Solution 9 - JavaMarlon BernardesView Answer on Stackoverflow
Solution 10 - JavaSIVA KUMARView Answer on Stackoverflow
Solution 11 - JavarzwitserlootView Answer on Stackoverflow
Solution 12 - JavaPPartisanView Answer on Stackoverflow
Solution 13 - JavaDaniel De LeónView Answer on Stackoverflow
Solution 14 - JavaAukeView Answer on Stackoverflow
Solution 15 - JavaAndras KloczlView Answer on Stackoverflow
Solution 16 - JavaJO3-W3B-D3VView Answer on Stackoverflow
Solution 17 - JavaRobin MathurView Answer on Stackoverflow
Solution 18 - JavaChanghoonView Answer on Stackoverflow