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

Ser*_*iev 247 java parsing uri namevaluecollection

我有这样的URI:

https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback
Run Code Online (Sandbox Code Playgroud)

我需要一个带有解析元素的集合:

NAME               VALUE
------------------------
client_id          SS
response_type      code
scope              N_FULL
access_type        offline
redirect_uri       http://localhost/Callback
Run Code Online (Sandbox Code Playgroud)

确切地说,我需要一个Java等价的C#HttpUtility.ParseQueryString方法.请给我一个建议.谢谢.

Pr0*_*m3r 319

如果您正在寻找一种方法来实现它而不使用外部库,以下代码将帮助您.

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;
}
Run Code Online (Sandbox Code Playgroud)

你可以使用<map>.get("client_id")你的问题中给出的URL 来访问返回的Map ,这将返回"SS".

添加了更新 URL解码

更新由于这个答案仍然非常流行,我对上述方法进行了改进,它使用相同的键和参数处理多个参数,没有任何值.

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;
}
Run Code Online (Sandbox Code Playgroud)

更新 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<>(key, value);
}
Run Code Online (Sandbox Code Playgroud)

使用URL运行上述方法

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

返回此地图:

{param1=["value1"], param2=[null], param3=["value3", null]}
Run Code Online (Sandbox Code Playgroud)

  • 您忘记解码名称和参数,这是让图书馆执行常见任务通常更好的原因之一. (12认同)
  • 事实上,你是对的...但我个人更喜欢自己编写这样的"简单"任务,而不是为我必须完成的每项任务使用自己的库. (10认同)
  • @Chris你对使用URL编码转义的xml/html感到困惑.您的示例网址应为:a.com/q?1=a%26b&2=b%26c (4认同)
  • 如果您使用静态导入,请显示它们 (3认同)
  • 如果您有多个具有相同名称/键的参数,则使用此函数将覆盖具有相似键的值. (2认同)
  • 最好指出使用了哪些函数:Collectors.mapping(...)和Collectors.toList(...) (2认同)

Jua*_*des 281

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());
}
Run Code Online (Sandbox Code Playgroud)

输出

one : 1
two : 2
three : 3
three : 3a
Run Code Online (Sandbox Code Playgroud)

注意:仅适用于HTTP PUT或HTTP POST

  • @Crystark从httpclient 4.3.3开始,具有重复名称的查询字符串不会抛出任何异常.它按预期工作.`System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"),"UTF-8"));`将打印_ [foo = bar,foo =巴兹] _. (10认同)
  • 请注意,如果您的URL中有两次相同的参数(即?a = 1&a = 2),URLEncodedUtils将抛出IllegalArgumentException (6认同)
  • 从Android 6开始,Apache HTTP客户端库已被删除.这意味着`URLEncodedUtils和`NameValuePair`不再可用(除非您按照[here](https://developer.android.com/about/versions/marshmallow/android-6.0-changes)所述向旧版Apache库添加依赖项的.html#行为Apache的HTTP客户端)). (4认同)
  • @SergeyShafiev将`List <NameValuePair>`转换为`Map <String,String>`Java没有哈希映射的括号访问权限是很简单的,它看起来像`map.get("one")`If你不知道该怎么做,应该是另一个问题(但首先尝试一下).我们更喜欢在SO处保持问题 (3认同)
  • 我可以通过名称接收值而不传递所有元素吗?我的意思是这样的: System.out.print(params["one"]); (2认同)

xxg*_*xxg 89

如果您使用的是Spring Framework:

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));
}
Run Code Online (Sandbox Code Playgroud)

你会得到:

param1: ab
param2: cd,ef
Run Code Online (Sandbox Code Playgroud)

  • 对于 URL,请使用“UriComponentsBuilder.fromHttpUrl(url)” (4认同)
  • 请注意,“getQueryParams()”不会**解码查询参数。因此,对于“http://foobar/path?param1=a%3Db”的 URL,您会得到“param1: a%3Db”,而不是“param1: a=b”。您需要自己使用“URLDecoder.decode()”... -“getQueryParams()”是**损坏**。 (2认同)

小智 48

使用谷歌番石榴,并在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

给你的

{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
Run Code Online (Sandbox Code Playgroud)

  • 所选答案中描述的URL解码怎么样? (17认同)
  • 警告!!这样做是不安全的,因为如果查询字符串中有重复键,`splitter.split()`将抛出`IllegalArgumentException`.请参阅http://stackoverflow.com/questions/1746507/authoritative-position-of-duplicate-http-get-query-keys (11认同)
  • 这也怀疑具有相同名称的多个键.根据javadocs,这将抛出IllegalArgumentException (7认同)
  • 而不是手动拆分`uri`你应该使用`new java.net.URL(uri).getQuery()`,因为这会在URL上获得免费的输入验证. (5认同)

Dmi*_*ich 25

我找到的最短路是这一个:

MultiValueMap<String, String> queryParams =
            UriComponentsBuilder.fromUriString(url).build().getQueryParams();
Run Code Online (Sandbox Code Playgroud)

更新: UriComponentsBuilder来自Spring.这里的链接.

  • 不知道这个UriComponentsBuilder类来自哪里它不是很有用. (3认同)
  • 可能值得注意的是,如果您已经在使用Spring,那么这只是一个好主意。如果您不使用Spring,则应避免使用。http://samatkinson.com/why-i-hate-spring/ (2认同)

THA*_*rum 14

对于Android,如果您在项目中使用OkHttp.你可以看看这个.它简单而有帮助.

final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
    final String target = url.queryParameter("target");
    final String id = url.queryParameter("id");
}
Run Code Online (Sandbox Code Playgroud)

  • 更新:从 OkHttp 4 开始,`HttpUrl.parse()` 已被弃用,但是使用此处描述的新 OkHttp 扩展功能仍然可以实现:/sf/answers/4418274241/ (2认同)

Fuw*_*jax 8

如果您正在使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但这是一个非常残酷的路线.


fre*_*dev 8

Java 8一个声明

给出要分析的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");
Run Code Online (Sandbox Code Playgroud)

此解决方案收集对的列表:

List<AbstractMap.SimpleEntry<String, String>> list = 
        Pattern.compile("&").splitAsStream(url.getQuery())
        .map(s -> Arrays.copyOf(s.split("="), 2))
        .map(o -> new AbstractMap.SimpleEntry<String, String>(decode(o[0]), decode(o[1])))
        .collect(toList());
Run Code Online (Sandbox Code Playgroud)

另一方面,该解决方案收集地图(假设在URL中可以存在具有相同名称但不同值的更多参数).

Map<String, List<String>> list = 
        Pattern.compile("&").splitAsStream(url.getQuery())
        .map(s -> Arrays.copyOf(s.split("="), 2))
        .collect(groupingBy(s -> decode(s[0]), mapping(s -> decode(s[1]), toList())));
Run Code Online (Sandbox Code Playgroud)

两种解决方案都必须使用效用函数来正确解码参数.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这更像是Java 8*方法*而不是Java 8 oneliner. (4认同)
  • IMO,一个oneliner应该是短的,不应跨越多行. (3认同)
  • 我想你可以在一行上写一整节课,但这不是“单行”这个词通常的意思。 (3认同)
  • 这里涉及多个语句。 (2认同)
  • 如果您碰巧拥有 Java 10 或更高版本,则有一个小小的改进 - URLDecoder#decode(最后)具有一个重载,它采用 Charset(例如 StandardCharsets.UTF_8)而不是字符串进行编码,这意味着您不需要捕获 UnsupportedEncodingException。 (2认同)

Ans*_*den 8

如果你正在使用servlet doGet试试这个

request.getParameterMap()
Run Code Online (Sandbox Code Playgroud)

返回此请求的参数的java.util.Map.

返回:一个不可变的java.util.Map,包含作为键的参数名称和作为映射值的参数值.参数映射中的键是String类型.参数映射中的值是String数组类型.

(Java doc)


Ram*_*shi 8

在 Android 上,包android.net 中有一个 Uri 类。请注意,Uriandroid.net 的一部分,而URIjava.net 的一部分。

Uri 类有许多函数可以从查询中提取键值对。 在此处输入图片说明

以下函数以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;
}
Run Code Online (Sandbox Code Playgroud)

在科特林:

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
    }
Run Code Online (Sandbox Code Playgroud)


Kir*_*rby 5

Netty还提供了一个很好的查询字符串解析器,称为QueryStringDecoder. 在一行代码中,它可以解析问题中的 URL。我喜欢,因为它不需要捕捉或投掷java.net.MalformedURLException

在一行中:

Map<String, List<String>> parameters = new QueryStringDecoder(url).parameters();
Run Code Online (Sandbox Code Playgroud)

请参阅此处的 javadoc: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)));
  }
}
Run Code Online (Sandbox Code Playgroud)

这产生

NAME               VALUE
------------------------
client_id          SS
response_type      code
scope              N_FULL
access_type        offline
redirect_uri       http://localhost/Callback
Run Code Online (Sandbox Code Playgroud)


Eni*_*igo 5

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)));
}
Run Code Online (Sandbox Code Playgroud)