将 URI 查询的字符串解析为键值对集合
我把URI弄成这样了。
https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback
我需要一个带有解析过的元素的集合。
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
确切地说,我需要一个与C#/.NET HttpUtility.ParseQueryString
方法等效的Java方法。
如果你正在寻找一种不使用外部库就能实现的方法,下面的代码会对你有所帮助。
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;
}
你可以使用<map>.get("client_id")
来获取,在你的问题中给出的URL,这将返回"SS"。
UPDATE URL-Decoding的添加
更新由于这个答案仍然很受欢迎,我对上面的方法做了一个改进版本,它可以处理具有相同键的多个参数,也可以处理没有值的参数。
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;
}
更新Java8版本
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)
);
}
运行上面的方法,其网址是
https://stackoverflow.com?param1=value1¶m2=¶m3=value3¶m3
返回该地图。
{param1=["value1"], param2=[null], param3=["value3", null]}
org.apache.http.client.utils.URLEncodedUtils
是一个众所周知的库,可以为你做这件事。
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());
}
输出
one : 1
two : 2
three : 3
three : 3a
List<NameValuePair>
转换为Map<String,String>
是很简单的,Java对哈希图没有括号访问,它看起来像map.get("one")
如果你不知道怎么做,应该是另一个问题(但先自己试试)。我们更喜欢保持问题的简洁性。
- Ruan Mendes 2012-11-27
System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));
会打印[foo=bar, foo=baz]。
- Akihiro HARAI 2014-06-02
URLEncodedUtils and
NameValuePair不再可用(除非你按照这里的描述向传统的Apache库添加一个依赖关系)。
- Ted Hopp 2015-12-10
如果你使用的是Spring框架,那么你就可以使用。
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));
}
你将会得到。
param1: ab
param2: cd,ef
UriComponentsBuilder.fromHttpUrl(url)
- Ilya Serbis 2020-02-11
getQueryParams()
不会解码查询参数。所以对于一个http://foobar/path?param1=a%3Db
的URL,你会得到param1: a%3Db
而不是param1: a=b
。你需要自己使用URLDecoder.decode()
...。- getQueryParams()
是BROKEN。
- Peter V. Mørch 2021-01-11
使用google Guava,用2行就可以完成。
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);
}
}
这给你带来了
{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
new java.net.URL(uri).getQuery()
,而不是手动分割uri
,因为这为你赢得了对URL的自由输入验证权。
- avgvstvs 2015-03-06
splitter.split()
将抛出IllegalArgumentException
。参见stackoverflow.com/questions/1746507/…。
- Anderson 2015-11-27
我发现的最短的方法是这个方法。
MultiValueMap<String, String> queryParams =
UriComponentsBuilder.fromUriString(url).build().getQueryParams();
更新: UriComponentsBuilder
来自Spring。这里链接。
getQueryParams()
对查询参数进行而不是解码。所以对于一个http://foobar/path?param1=a%3Db
的URL,你会得到param1: a%3Db
而不是param1: a=b
。你需要自己使用URLDecoder.decode()
...。- getQueryParams()
是BROKEN。
- Peter V. Mørch 2021-01-11
org.springframework.web.util.HtmlUtils.htmlUnescape(myUrl)
- Michael Böckling 2022-09-14
对于Android,如果你在你的项目中使用OkHttp。你可以看一下这个。它简单而有帮助。
final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
final String target = url.queryParameter("target");
final String id = url.queryParameter("id");
}
HttpUrl.parse()
已经被废弃了,但是用这里描述的新的OkHttp扩展函数仍然可以做到这一点。stackoverflow.com/a/63118203/2888763
- Trevor Halvorson 2021-10-03
Java 11
给出了要分析的URL。
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");
这个解决方案收集了一个成对的列表。
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());
另一方面,这个解决方案收集了一个Map(考虑到在一个URL中可能有更多的同名但不同值的参数)。
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())));
两种解决方案都必须使用一个实用的函数来正确解码参数。
private static String decode(final String encoded) {
return Optional.ofNullable(encoded)
.map(e -> URLDecoder.decode(e, StandardCharsets.UTF_8))
.orElse(null);
}
如果你使用的是servlet doGet,请试着这样做
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)
HttpServletRequest
类型的参数,在Mock MVC单元测试中,它也适用于MockHttpServletRequest
类型。
- GameSalutes 2019-05-23
在Android上,在包android.net中有一个Uri类。请注意,Uri是android.net的一部分,而URI是java.net的一部分。
下面的函数以HashMap的形式返回键值对。
在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;
}
在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
}
Netty也提供了一个很好的查询字符串解析器,称为QueryStringDecoder
。在一行代码中,它可以解析问题中的URL。 我喜欢,因为它不需要捕捉或抛出java.net.MalformedURLException
。
用一句话来说就是。
Map<String, List<String>> parameters = new QueryStringDecoder(url).parameters();
请看这里的javadocs。https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html
下面是一个简短的、自成一体的、正确的例子。
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)));
}
}
这就产生了
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
如果你使用的是Java 8,并且你愿意写一些可重用的方法,那么你可以用一行字来完成它。
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);
}
}
但这是一条相当残酷的线。
有一个新版本的Apache HTTP客户端--org.apache.httpcomponents.client5
--其中URLEncodedUtils
现在已经被废弃。应该使用URIBuilder
来代替。
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)));
}
一个现成的URI查询部分解码的解决方案(包括解码和多参数值)。
评论内容
我对@Pr0gr4mm3r在https://stackoverflow.com/a/13592567/1211082中提供的代码并不满意。基于流的解决方案不做URLDecoding,可变的版本很笨重。
因此,我阐述了一个解决方案,即
- 可以将URI的查询部分分解为
Map<String, List<Optional<String>>>
- 可以为同一参数名处理多个值。
- 可以正确地表示没有数值的参数(
Optional.empty()
而不是null
)。 - 通过
URLdecode
对参数名称和值进行正确的解码。 - 是基于Java 8 Streams的。
- 是可以直接使用的(见下面的代码,包括进口)。
- 允许适当的错误处理(这里通过将一个检查过的异常
UnsupportedEncodingException
变成一个运行时异常RuntimeUnsupportedEncodingException
,允许与流相互作用。(将普通函数包装成抛出检查过的异常的函数是很麻烦的。而且ScalaTry
在Java语言默认情况下是不可用的)。
Java代码
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中的解决方案,它以简洁和美观而著称
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))
}
}
使用上面提到的评论和解决方案,我正在使用Map<String, Object>存储所有的查询参数,其中Objects可以是字符串或Set<String>。下面给出了解决方案。建议先使用某种url验证器来验证url,然后再调用convertQueryStringToMap方法。
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;
}
}
Set
是错误的数据类型。
- Peter V. Mørch 2021-01-11
我试着做了一个Kotlin版本,看到这个版本在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
}
而扩展方法则是
fun List<String>.getOrEmpty(index: Int) : String {
return getOrElse(index) {""}
}
fun String.decodeToUTF8(): String {
URLDecoder.decode(this, "UTF-8")
}
另外,我推荐基于regex的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 "";
}
}
这个类很容易使用。它在初始化时只需要URL或查询字符串,并通过给定的键解析值。
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的回答,最初参考了https://stackoverflow.com/a/51024552/3286489,但通过整理代码进行了改进,并提供了2个版本,并使用不可变的集合操作。
使用java.net.URI
来提取查询。然后使用下面提供的扩展函数
- 假设你只想得到查询的最后一个值,即
page2&page3
会得到{page=3}
,请使用下面的扩展函数
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"
- 假设你想得到一个查询的所有值的列表,即
page2&page3
会得到{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"
其使用方法如下
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}
当你的查询有单一的参数定义时,有很多答案可以适用于你的查询,正如你所指出的。在某些应用中,处理一些额外的查询参数的边缘情况可能是有用的,例如。
- 参数值的列表,如
param1¶m1=value¶m1=
意味着param1
被设置为List.of("", "value", "")
。 - 无效的排列组合,如
querypath?&=&&=noparamname&
。 - 在地图中使用空字符串而不是空值
a=
意味着"a"是List.of("")
,以匹配网络服务程序的处理。
这使用了一个带有过滤器和groupingBy的Stream来收集到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())));
}
如果你使用的是Spring,在你的控制器方法中添加一个RequestParam Map<String,String>
类型的参数,Spring就会为你构建地图。
仅仅是对Java 8版本的更新
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和toList()方法必须与Collectors一起使用,这一点在顶部的答案中没有提到。否则会在IDE中出现编译错误
splitQueryParameters()
方法?还有,**Collectors**
是怎么回事?
- Kirby 2019-05-15
在这里回答,因为这是个热门话题。这是在Kotlin中的一个干净的解决方案,使用了推荐的UrlQuerySanitizer
api。见官方文档。我已经添加了一个字符串生成器来连接和显示参数。
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")
}
下面是我的解决方案,其中有reduce和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;
}
- 我忽略了重复的参数(我采取的是最后一个)。
- 我使用
Optional<SimpleImmutableEntry<String, String>>
忽略了后来的垃圾。 - 缩减从一个空的地图开始,然后在每个SimpleImmutableEntry上填充它
如果你问,reduce在最后一个参数中需要这个奇怪的组合器,它只用于并行流中。它的目标是合并两个中间结果(这里是HashMap)。
如果你碰巧在classpath上有cxf-core,并且你知道你没有重复的查询参数,你可能想使用UrlUtils.parseQueryString。
Eclipse Jersey REST框架通过UriComponent
支持这一点。例子。
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));
}
如果只是想让URL后面的参数来自一个字符串。那么下面的代码将工作。我只是假设简单的Url。我的意思是没有硬性的检查和解码。就像在我的一个测试案例中,我得到了Url,我知道我只需要参数的值。这个URL很简单。不需要编码解码。
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);
谢谢你
对我来说,这似乎是整洁的最佳方式。
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)
);
}
前提是你的查询语法不允许有重复的参数。
org.keycloak.common.util.UriUtils
我不得不在一个Keycloak扩展中解析URI和查询参数,并发现这个实用类非常有用。
org.keycloak.common.util.UriUtils:
static MultivaluedHashMap<String,String> decodeQueryString(String queryString)
还有一个有用的方法,可以删除一个查询参数。
static String stripQueryParam(String url, String name)
为了解析URL,有一个org.keycloak.common.util.KeycloakUriBuilder。
KeycloakUriBuilder uri(String uriTemplate)
String getQuery()
和许多其他的好东西。