Parse a URI String into Name-Value Collection

前端 未结 19 2456
难免孤独
难免孤独 2020-11-22 01:34

I\'ve got the URI like this:

https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_         


        
相关标签:
19条回答
  • 2020-11-22 01:58

    If you are using servlet doGet try this

    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)

    0 讨论(0)
  • 2020-11-22 01:59

    Java 8 one statement

    Given the URL to analyse:

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

    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).

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

    Both the solutions must use an utility function to properly decode the parameters.

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 02:00

    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

    Use java.net.URI to extract the Query. Then use the below provided extension functions

    1. Assuming you only want the last value of query i.e. page2&page3 will get {page=3}, use the below extension function
        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 i.e. page2&page3 will get {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}
    
    0 讨论(0)
  • 2020-11-22 02:01

    I had a go at a Kotlin version seeing how this is the top result in 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")
    }
    
    0 讨论(0)
  • 2020-11-22 02:02

    For Android, if you are using OkHttp in your project. 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");
    }
    
    0 讨论(0)
  • 2020-11-22 02:03

    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".

    UPDATE URL-Decoding added

    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

    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, "UTF-8"),
            URLDecoder.decode(value, "UTF-8")
        );
    }
    

    Running the above method with the URL

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

    returns this Map:

    {param1=["value1"], param2=[null], param3=["value3", null]}
    
    0 讨论(0)
提交回复
热议问题