您知道任何可以将Map转换为URL友好查询字符串的实用程序类/库吗?
例:
我有一张地图:
- “param1”= 12,
- “param2”=“cat”
我想得到:param1=12¶m2=cat
。
PS。我知道我可以轻松地自己编写,我很惊讶我无法在任何地方找到它(我到目前为止检查了Apache Commons)。
答案 0 :(得分:60)
我现在看到的最强大的是来自URLEncodedUtils的Apache Http Compoments类(HttpClient 4.0)。
方法URLEncodedUtils.format()
就是您所需要的。
它不使用map,因此您可以拥有重复的参数名称,例如
a=1&a=2&b=3
不是我建议使用这种参数名称。
答案 1 :(得分:40)
这是我很快写的东西;我相信它可以改进。
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"
}
}
答案 2 :(得分:26)
我找到了一个使用java 8和polygenelubricants&#39;溶液
parameters.entrySet().stream()
.map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue()))
.reduce((p1, p2) -> p1 + "&" + p2)
.orElse("");
答案 3 :(得分:14)
2016年6月更新
感到有必要添加一个答案,已经看到太多的SOF答案,对于非常常见的问题的日期或不充分的答案 - 一个好的库和parse
和format
操作的一些可靠的示例用法。 / p>
使用org.apache.httpcomponents.httpclient库。该库包含此org.apache.http.client.utils.URLEncodedUtils类实用程序。
例如,很容易从Maven下载此依赖项:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
出于我的目的,我只需要parse
(从查询字符串读取到名称 - 值对)和format
(从名称 - 值对读取查询字符串)查询字符串。但是,对于使用URI执行相同操作也是等效的(请参阅下面的注释掉的行)。
//必需的导入
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
}
这个库正是我所需要的,并且能够替换一些被黑客入侵的自定义代码。
答案 4 :(得分:13)
在Spring Util中,有一种更好的方法..,
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);
答案 5 :(得分:10)
如果您确实要构建完整的URI,请尝试URIBuilder中的Apache Http Compoments(HttpClient 4)。
这实际上并没有回答这个问题,但它回答了我在发现这个问题时的问题。
答案 6 :(得分:7)
另一个'一类'/无依赖方式,处理单个/多个:
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);
}
}
}
您可以使用简单(在大多数情况下更容易编写)或在需要时使用多个。请注意,两者都可以通过添加&符组合... 如果您发现任何问题,请在评论中告诉我。
答案 7 :(得分:7)
我想使用java 8 mapping和reduce来构建@ eclipse的答案。
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("");
}
额外的map
操作采用简化字符串,只有在字符串存在时才将?
放在前面。
答案 8 :(得分:4)
这是我使用Java 8和org.apache.http.client.URLEncodedUtils
实现的解决方案。它将地图的条目映射到BasicNameValuePair
列表,然后使用Apache的URLEncodedUtils
将其转换为查询字符串。
List<BasicNameValuePair> nameValuePairs = params.entrySet().stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
URLEncodedUtils.format(nameValuePairs, Charset.forName("UTF-8"));
答案 9 :(得分:1)
为了改善@ eclipse的答案:在Javaland中,请求参数映射通常表示为Map<String, String[]>
,Map<String, List<String>>
或可能某种MultiValueMap<String, String>
同一件事情。在任何情况下:参数通常可以有多个值。因此,Java 8解决方案就是这样的:
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);
}
}
答案 10 :(得分:1)
java没有内置功能可以做到这一点。但是,嘿,java是一种编程语言,所以..让我们对其进行编程吧!
map.values().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"))
这将为您提供“ param1 = 12&param2 = cat”。现在,我们需要将URL和这一点结合在一起。您可能认为可以这样做:URL + "?" + theAbove
,但是,如果URL已经包含问号,则必须将其全部与“&”连接起来。一种检查方法是查看网址中是否已经存在问号。
此外,我不太了解您的地图中有什么。如果是原始内容,则可能必须使用e.getKey()
或类似的方法来保护对e.getValue()
和URLEncoder.encode
的调用。
另一种可供选择的方法是,放宽视野。您是在尝试将地图的内容附加到URL上,还是...您是在试图从Java进程发出HTTP(S)请求,并将地图中的内容作为(附加)HTTP参数?在后一种情况下,您可以查看类似OkHttp的http库,该库具有一些不错的API可以完成此工作,然后您就可以放弃任何需要首先处理该URL的需求。
答案 11 :(得分:1)
您可以为此使用Stream
,但是我自己使用Uri.Builder
而不是附加查询参数。例如:
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);
答案 12 :(得分:1)
我认为这对内存使用和性能更好,并且我想仅在属性值为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();
}
答案 13 :(得分:0)
就个人而言,我会寻求这样的解决方案,它与@rzwitserloot提供的解决方案非常相似,只是细微的差别。
此解决方案小巧,简单且干净,在依赖性方面几乎不需要,所有这些都是Java Util软件包的一部分。
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));
答案 14 :(得分:0)
使用EntrySet和流:
map
.entrySet()
.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining("&"));
答案 15 :(得分:0)
对于多值地图,您可以执行以下操作(使用Java 8流api)
已考虑到网址编码。
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("&"));
答案 16 :(得分:0)
科特琳
mapOf(
"param1" to 12,
"param2" to "cat"
).map { "${it.key}=${it.value}" }
.joinToString("&")