简体   繁体   English

将 URI 字符串解析为名称-值集合

[英]Parse a URI String into Name-Value Collection

I've got the URI like this:我有这样的 URI:

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.确切地说,我需要一个 Java 等效于 C#/.NET HttpUtility.ParseQueryString方法。

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".您可以使用<map>.get("client_id")访问返回的地图,使用问题中给出的 URL,这将返回“SS”。

UPDATE URL-Decoding added添加了更新URL 解码

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

Running the above method with the URL使用 URL 运行上述方法

https://stackoverflow.com?param1=value1&param2=&param3=value3&param3

returns this Map:返回此地图:

{param1=["value1"], param2=[null], param3=["value3", null]}

org.apache.http.client.utils.URLEncodedUtils 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:如果您使用的是 Spring 框架:

public static void main(String[] args) {
    String uri = "http://my.test.com/test?param1=ab&param2=cd&param2=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:使用 google Guava 并分两行完成:

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.更新: UriComponentsBuilder来自 Spring。 Here the link .这里是链接

For Android, if you are using OkHttp in your project.对于 Android,如果您在项目中使用OkHttp 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纯Java 11

Given the URL to analyse:给定要分析的 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");

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).另一方面,此解决方案收集地图(假设在 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())));

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

If you are using servlet doGet try this如果您使用的是 servlet doGet 试试这个

request.getParameterMap()

Returns a java.util.Map of the parameters of this request.返回此请求参数的 java.util.Map。

Returns: an immutable java.util.Map containing parameter names as keys and parameter values as map values.返回: 一个不可变的 java.util.Map,其中包含作为键的参数名称和作为映射值的参数值。 The keys in the parameter map are of type String.参数映射中的键是字符串类型。 The values in the parameter map are of type String array.参数映射中的值是字符串数组类型。

( Java doc ) Java 文档

On Android, there is a Uri class in package android.net .在 Android 上,包android.net中有一个 Uri 类。 Note that Uri is part of android.net , whereas URI is part of java.net .请注意, Uriandroid.net的一部分,而URIjava.net的一部分。

Uri class has many functions to extract key-value pairs from a query. Uri 类有许多函数可以从查询中提取键值对。 在此处输入图像描述

Following function returns key-value pairs in the form of HashMap.以下函数以 HashMap 的形式返回键值对。

In Java:在 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're using Java 8 and you're willing to write a few reusable methods, you can do it in one line.如果您使用的是 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);
    }
}

But that's a pretty brutal line.但这是一条非常残酷的路线。

Netty also provides a nice query string parser called QueryStringDecoder . Netty还提供了一个很好的查询字符串解析器,称为QueryStringDecoder In one line of code, it can parse the URL in the question.在一行代码中,它可以解析问题中的 URL。 I like because it doesn't require catching or throwing java.net.MalformedURLException .我喜欢,因为它不需要捕获或抛出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在此处查看 javadocs: 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

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>.使用上述注释和解决方案,我使用 Map<String, Object> 存储所有查询参数,其中对象可以是字符串或 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.建议先使用某种 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;
    }
}

I had a go at a Kotlin version seeing how this is the top result in Google.我尝试了 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
}

And the extension methods以及扩展方法

fun List<String>.getOrEmpty(index: Int) : String {
    return getOrElse(index) {""}
}

fun String.decodeToUTF8(): String { 
    URLDecoder.decode(this, "UTF-8")
}

A ready-to-use solution for decoding of URI query part (incl. decoding and multi parameter values)用于解码 URI 查询部分(包括解码和多参数值)的即用型解决方案

Comments注释

I wasn't happy with the code provided by @Pr0gr4mm3r in https://stackoverflow.com/a/13592567/1211082 .我对 @Pr0gr4mm3r 在https://stackoverflow.com/a/13592567/1211082中提供的代码不满意。 The Stream-based solution does not do URLDecoding, the mutable version clumpsy.基于 Stream 的解决方案不执行 URLDecoding,可变版本笨拙。

Thus I elaborated a solution that因此,我制定了一个解决方案

  • Can decompose a URI query part into a Map<String, List<Optional<String>>>可以将 URI 查询部分分解为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 )可以正确表示没有值的参数Optional.empty()而不是null
  • Decodes parameter names and values correctly via URLdecode通过URLdecode正确解码参数名称
  • Is based on Java 8 Streams基于 Java 8 Streams
  • Is directly usable (see code including imports below)可直接使用(请参阅下面包含导入的代码)
  • Allows for proper error handling (here via turning a checked exception UnsupportedEncodingException into 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.)允许正确的错误处理(这里通过将检查异常UnsupportedEncodingException转换为运行时异常RuntimeUnsupportedEncodingException允许与流交互。(将常规函数包装到抛出检查异常的函数中是一种痛苦。Scala Try在 Java 语言默认值中不可用。)

Java Code 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 Code斯卡拉代码

... and for the sake of completeness I can not resist to provide the solution in Scala that dominates by brevity and beauty ...为了完整起见,我忍不住要在 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))
  }
}

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 Kotlin 的答案,最初参考来自https://stackoverflow.com/a/51024552/3286489 ,但通过整理代码并提供了 2 个版本,并使用不可变集合操作来改进版本

Use java.net.URI to extract the Query.使用java.net.URI提取查询。 Then use the below provided extension functions然后使用下面提供的扩展功能

  1. Assuming you only want the last value of query ie page2&page3 will get {page=3} , use the below extension function假设您只想要查询的最后一个值,即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"
  1. Assuming you want a list of all value for the query ie page2&page3 will get {page=[2, 3]}假设您想要查询的所有值的列表,即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"

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&param1=value&param1= meaning param1 is set to List.of("", "value", "")参数值列表,例如param1&param1=value&param1=表示param1设置为List.of("", "value", "")
  • invalid permutations such as querypath?&=&&=noparamname& .无效排列,例如querypath?&=&&=noparamname&
  • use empty string not null in maps a= means "a" is List.of("") to match web servlet handling在映射中使用非空字符串a=表示“a”是List.of("")以匹配 Web servlet 处理

This uses a Stream with filters and groupingBy to collect to Map<String, List<String>> :这使用带有过滤器和 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())));
}

There a new version of Apache HTTP client - org.apache.httpcomponents.client5 - where URLEncodedUtils is now deprecated.有一个新版本的 Apache HTTP 客户端 - org.apache.httpcomponents.client5 - 现在不推荐使用URLEncodedUtils URIBuilder should be used instead:应该使用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)));
}

Also, I would recommend regex based implementation of URLParser另外,我会推荐基于正则表达式的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.这个 class 易于使用。 It just needs the URL or the query string on initialization and parses value by given key.它只需要 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
    }
}

如果您使用的是 Spring,请将@RequestParam Map<String,String>类型的参数添加到您的控制器方法中,Spring 将为您构建地图!

Just an update to the Java 8 version只是对 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 and toList() methods have to be used with Collectors which was not mentioned in the top answer. mapping 和 toList() 方法必须与顶级答案中未提及的收集器一起使用。 Otherwise it would throw compilation error in IDE否则会在IDE中抛出编译错误

Answering here because this is a popular thread.在这里回答,因为这是一个流行的线程。 This is a clean solution in Kotlin that uses the recommended UrlQuerySanitizer api.这是 Kotlin 中使用推荐的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 :这是我使用reduceOptional的解决方案:

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我稍后使用Optional<SimpleImmutableEntry<String, String>>来忽略垃圾
  • The reduction start with an empty map, then populate it on each SimpleImmutableEntry减少从一个空映射开始,然后在每个 SimpleImmutableEntry 上填充它

In case you ask, reduce requires this weird combiner in the last parameter, which is only used in parallel streams.如果你问, reduce需要在最后一个参数中使用这个奇怪的组合器,它只在并行流中使用。 Its goal is to merge two intermediate results (here HashMap).它的目标是合并两个中间结果(这里是 HashMap)。

如果你碰巧在类路径上有 cxf-core 并且你知道你没有重复的查询参数,你可能想要使用UrlUtils.parseQueryString

I've got the URI like this:我有这样的URI:

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.确切地说,我需要C#/。NET HttpUtility.ParseQueryString方法的Java等效HttpUtility.ParseQueryString

The Eclipse Jersey REST framework supports this through UriComponent . Eclipse Jersey REST 框架通过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.如果只需要来自字符串的 URL 之后的参数。 Then the following code will work.然后以下代码将起作用。 I am just assuming the simple Url.我只是假设简单的 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.就像在我的一个测试用例中一样,我得到了 Url,我知道我只需要参数的值。 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&param2=value2&param3=value3";
String location2 = "https://stackoverflow.com?param1=value1&param2=&param3=value3&param3";
    
    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.前提是您的查询语法不允许重复参数。

org.keycloak.common.util.UriUtils org.keycloak.common.util.UriUtils

I had to parse URIs and Query Parameters in a Keycloak extension and found this utility classes very useful:我不得不在 Keycloak 扩展中解析 URI 和查询参数,发现这个实用程序类非常有用:

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:要解析 URL,有 org.keycloak.common.util.KeycloakUriBuilder:

KeycloakUriBuilder  uri(String uriTemplate)
String  getQuery()

and lots of other goodies.还有很多其他的好东西。

The Hutool framework supports this through HttpUtil. Hutool框架通过 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一个kotlin版本

of the answer Answer by matthias provided由 matthias提供的答案

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.以及。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM