你知道是否有任何实用的类或库,可以将Map转换为URL友好的查询字符串吗?
例如:
我有一个Map:
"param1"=12,
"param2"="cat"
我想要获取:
param1=12¶m2=cat
最终输出
relativeUrl+param1=12¶m2=cat
你知道是否有任何实用的类或库,可以将Map转换为URL友好的查询字符串吗?
例如:
我有一个Map:
"param1"=12,
"param2"="cat"
我想要获取:
param1=12¶m2=cat
最终输出
relativeUrl+param1=12¶m2=cat
我看到的最强大的现成工具是来自Apache Http Compoments (HttpClient 4.0) 的 URLEncodedUtils 类。
你需要使用的方法是URLEncodedUtils.format()
。
它不使用映射,因此您可以有重复的参数名称,例如:
a=1&a=2&b=3
虽然我不建议这种使用参数名称的方式。
我使用Java 8和Polygenelubricants的方案找到了一个顺畅的解决方案。
parameters.entrySet().stream()
.map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue()))
.reduce((p1, p2) -> p1 + "&" + p2)
.orElse("");
?
附加到从中返回的字符串,并附加到我的URL上。 - yiweitoto[]=4&toto[]=5
- Thomas DecauxURLEncoder.encode(String s, Charset charset)
,不再抛出UnsupportedEncodingException
异常,因此可以简化map调用而无需调用辅助方法。 - Johannes Brodwall这是我草草写的代码,肯定还有改进的地方。
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"
}
}
Map<String, List<String>>
或Map<String, String[]>
。同一名称可以有多个值。不确定原始发布者是否聪明地设计了它。 - BalusCURLEncoder.encode(String s, Charset charset)
,不需要处理UnsupportedEncodingException
。 - Johannes Brodwall在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);
UriComponentsBuilder
- MaxZoomUriComponentsBuilder.fromHttpUrl
之前省略的空值MultiValueMap。当我添加了params.values().removeIf(entry -> entry.getValue() == null);
时,它没有起作用。有人知道为什么我不能使用上述语法从MultiValueMap中删除条目吗? - OrbyMap
转换为 String
,而不是一个 UriComponents
对象。但对于那些只想发出 HTTP 请求的人来说,这很有用。 - axiopistyUriComponentsBuilder
: 它过于复杂,处理URL查询值的编码存在问题(虽然是有意为之),而且与仅使用params.map { "${urlEncode(it.first)}=${urlEncode(it.second)}" }.joinToString("&")
相比并没有太多附加价值。 - Ondra Žižka2016年6月更新
看到太多SO问题的回答使用过时或不充分的方法去解决很常见的问题,我觉得有必要添加一个答案 - 使用一个好的库和一些实例用法来处理parse
和format
操作。
使用 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>
为了我的目的,我只需要解析
(从查询字符串中读取名称-值对)和格式化
(从名称-值对中读取查询字符串)查询字符串。但是,也可以使用等效方法处理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;
// 代码片段
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
}
这个库完全符合我的需求,能够替代一些被毁坏的自定义代码。
我希望在 @eclipse 的答案上进行补充,使用 Java 8 的映射和归约。
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
操作会将被缩减的字符串作为输入,并且只在该字符串存在时在前面加上一个?
。另一个实现它的方法是'单一类'/无依赖关系方式,处理单个/多个:
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);
}
}
}
当需要时,您可以使用简单的方法(在大多数情况下更易编写)或多种方法。请注意,通过添加 & 符号可以组合两者... 如果您发现任何问题,请在评论中让我知道。
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"));
compile 'org.apache.httpcomponents:httpclient:4.5.2'
。 - Miguel ReyesJava本身没有内置实现此功能的方法。但是,由于Java是一种编程语言,我们可以通过编程来实现它!
map
.entrySet()
.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining("&"))
这样就给了你param1=12¶m2=cat
。现在我们需要将URL和这段内容拼接起来。你可能会认为可以直接这样做:URL + "?" + theAbove
,但如果URL中已经包含问号,你就必须使用"&"将它们连接起来。一种检查的方法是查看URL中是否已经有问号了。
此外,我不太清楚你的映射中有什么内容。如果它是原始的东西,你可能需要用URLEncoder.encode
或类似方法保护对e.getKey()
和e.getValue()
的调用。
另一种方法是采取更广泛的视角。你是想将一个映射的内容附加到一个URL上,还是想从Java进程中使用映射中的内容作为(额外的)HTTP参数发出HTTP(S)请求?在后一种情况下,你可以查看像OkHttp这样的HTTP库,它具有一些很好的API来完成这项工作,然后你可以避免在第一次操作中对URL进行任何干扰。