Проаналізуйте рядок URI в колекції імен-значень


274

У мене такий 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

Якщо бути точним, мені потрібен еквівалент Java для методу C # /. NET HttpUtility.ParseQueryString .

Будь ласка, дайте мені поради з цього приводу.

Дякую.



@MattBall Якщо OP використовує Android, то це
Хуан Мендес

156
Чи не розумно, що це не є частиною API API java.net.URI/ java.net.URL?
Dilum Ranatunga

Будь ласка , перевірте це рішення - твердий бібліотека і робочий приклад для обох розбору і форматування операцій: stackoverflow.com/a/37744000/1882064
arcseldon

Відповіді:


342

Якщо ви шукаєте спосіб досягти цього без використання зовнішньої бібліотеки, вам допоможе наступний код.

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

ОНОВЛЕНО Розшифровка 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;
}

ОНОВЛЕННЯ версії 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);
}

Запуск вищевказаного методу з URL-адресою

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

повертає цю карту:

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

13
Ви забуваєте розшифрувати імена та параметри, одна з причин, чому зазвичай краще дозволити бібліотекам робити звичайні завдання.
Хуан Мендес

10
Дійсно, ти маєш рацію ... але я особисто вважаю за краще писати такі "легкі" завдання самостійно, а не використовувати власну бібліотеку для кожного окремого завдання, яке мені потрібно виконати.
Pr0gr4mm3r

2
якщо у вас кілька параметрів з одним іменем / ключем, використання цієї функції замінить значення, яке має аналогічний ключ.
snowball147

4
@Chris Ви плутаєте втечу xml / html з кодуванням URL-адрес. Ваша прикладна URL-адреса повинна бути: a.com/q?1=a%26b&2=b%26c
sceaj

3
було б непогано вказати, які функції використовуються: Collectors.mapping (...) та Collectors.toList (...)
Thomas Rebele

311

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

Чи можу я отримати значення за назвою, не передаючи всі елементи? Я маю на увазі щось подібне: System.out.print (params ["one"]);
Сергій Шафієв

3
@SergeyShafiev Перехід List<NameValuePair>до Map<String,String>Java не має дугового доступу для хеш-карт, це виглядатиме так: map.get("one")Якщо ви не знаєте, як це зробити, це має бути іншим питанням (але спробуйте це самостійно) . Ми вважаємо за краще, щоб питання були тонкими тут на SO
Хуан Мендес

6
Будьте уважні, якщо у вас в URL-адресі два рази один і той же параметр (тобто? A = 1 & a = 2) URLEncodedUtils викине IllegalArgumentException
Crystark

10
@Crystark Станом на httpclient 4.3.3, рядок запиту із скопійованими іменами не виключає жодних винятків. Це працює як очікувалося. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));надрукує [foo = bar, foo = baz] .
Акіхіро ХАРАЙ

4
Станом на Android 6, клієнтська бібліотека Apache HTTP була видалена. Це означає, що URLEncodedUtils and NameValuePair` більше не доступні (якщо ви не додасте залежність до спадкової бібліотеки Apache, як описано тут ).
Тед Хопп

108

Якщо ви використовуєте 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));
}

Ти отримаєш:

param1: ab
param2: cd,ef

1
для використання URL-адресUriComponentsBuilder.fromHttpUrl(url)
Lu55,

51

скористайтеся 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}

18
Як щодо розшифровки URL-адреси, описаної у вибраній відповіді?
Клінт Іствуд

7
Це також підозра на кілька клавіш з тим самим іменем. За словами javadocs, це кине нелегальний аргументException
Jontro

5
Замість розбиття вручну uriви повинні використовувати, new java.net.URL(uri).getQuery()оскільки це купує вам безкоштовну перевірку введення URL-адреси.
avgvstvs

1
Для розшифровки: кінцева карта <String, String> queryVars = Maps.transformValues ​​(карта, нова функція <String, String> () застосовується {@Override public String (String value) {try {return URLDecoder.decode (значення, "UTF- 8 ");} улов (UnsupportedEncodingException e) {// TODO Автоматично створений блок лову e.printStackTrace ();} повернене значення;}});
freakhead

11
УВАГА!! НЕ БЕЗПЕЧНО це робити, оскільки splitter.split()буде викинуто, IllegalArgumentExceptionякщо в рядку запиту є дублікат ключа. Дивіться stackoverflow.com/questions/1746507/…
Андерсон,

31

Найкоротший шлях, який я знайшов, це такий:

MultiValueMap<String, String> queryParams =
            UriComponentsBuilder.fromUriString(url).build().getQueryParams();

ОНОВЛЕННЯ: UriComponentsBuilder походить з весни. Ось посилання .


3
Не знаючи, звідки цей клас UriComponentsBuilder, це не дуже корисно.
Томас Мортань

3
Можливо, варто відзначити, що це лише гарна ідея, якщо ви вже використовуєте Spring. Якщо ви не використовуєте Spring, ви хочете уникнути. samatkinson.com/why-i-hate-spring
Нік

1
Примітка. Для цього потрібні URI. Версія Java URI не є сукупністю URL-адрес (саме тому toURI може викидати винятки).
Адам Гент

18

Для Android, якщо ви використовуєте OkHttp у своєму проекті. Ви можете подивитися на це. Це просто і корисно.

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

HttpUrl - це якесь дивне ім'я, але саме це мені і було потрібно. Дякую.
GuiSim

10

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

Це рішення збирає список пар:

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

З іншого боку, це рішення збирає карту (враховуючи, що в 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())));

Обидва рішення повинні використовувати функцію утиліти, щоб правильно декодувати параметри.

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

4
Це скоріше підхід Java 8, а не oneliner Java 8.
Стефан

@Stephan добре :) можливо, і те й інше. Але мені більше цікаво зрозуміти, чи подобається вам це рішення.
freedev

3
IMO, oneliner повинен бути коротким і не повинен переходити на кілька рядків.
Стефан

1
Тут задіяно кілька заяв.
Стефан

2
Я думаю, ви могли написати цілий клас на одному рядку, але це не те, що зазвичай розуміється під фразою "однолінійний".
Abhijit Sarkar

10

Якщо ви використовуєте сервлет doGet, спробуйте це

request.getParameterMap()

Повертає java.util.Карта параметрів цього запиту.

Повертає: незмінний java.util.Map, що містить назви параметрів у вигляді ключів, а параметри як значення карт. Ключі на карті параметрів мають тип String. Значення в карті параметрів мають тип String array.

( Документ Java )


Це працює з Spring Web, а також у контролері ви можете мати параметр типу, HttpServletRequestі він працює, MockHttpServletRequestяк і в тестах Mock MVC unit.
GameSalutes

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

Але це досить жорстока лінія.


3

На Android є клас Uri в пакеті android.net . Зауважте, що Uri є частиною android.net , а URI - частиною java.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;
}

У Котліні:

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
    }

2

Використовуючи вищезазначені коментарі та рішення, я зберігаю всі параметри запиту за допомогою Map <String, Object>, де об'єкти можуть бути string або 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;
    }
}

2

Я ходив на версію Котліна, бачачи, як це найкращий результат в 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")
}

1
Рівна заслуга stackoverflow.com/users/1203812/matthew-herod 50/50 зусиль, але не може бути співавтором.
Грем Сміт

2

Готове до використання рішення для декодування частини запиту URI (включаючи декодування та значення кількох параметрів)

Коментарі

Я не був задоволений кодом, наданим @ Pr0gr4mm3r в https://stackoverflow.com/a/13592567/1211082 . Рішення, що базується на потоці, не робить URLDecoding, незграбна версія незграбної версії.

Таким чином, я розробив рішення, яке

  • Може розкласти частину запиту URI на a Map<String, List<Optional<String>>>
  • Може обробляти декілька значень для одного імені параметра
  • Може правильно представляти параметри без значення ( Optional.empty()замість null)
  • Імена параметрів Декодує і значення правильно черезURLdecode
  • Базується на потоках Java 8
  • Використовується безпосередньо (див. Код, включаючи імпорт нижче)
  • Дозволяє правильне поводження з помилками (тут, перетворюючи перевірений виняток UnsupportedEncodingExceptionу виняток із виконання часу, RuntimeUnsupportedEncodingExceptionщо дозволяє взаємодіяти з потоком. (Обгортання звичайної функції у функції, що передають перевірені винятки, це біль. А Scala Tryнедоступна за замовчуванням на мові 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;
        }
    }
}

Код Скали

... і заради повноти я не можу втриматися, щоб надати рішення в Скалі, яке домінує стислістю та красою

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

1

Просто оновлення до версії 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())));
}

Методи картографування та toList () повинні використовуватися з колекціонерами, що не було зазначено у верхній відповіді. Інакше це призведе до помилки компіляції в IDE


схоже, вам також потрібно поділитися своїм splitQueryParameters()методом? А що з **Collectors**?
Кірбі

1

Відповідь Котліна з початковою посиланням від https://stackoverflow.com/a/51024552/3286489 , але з покращеною версією, прибираючи коди та надаючи 2 його версії, та використовуйте незмінні операції збору

Використовуйте java.net.URIдля вилучення запиту. Потім скористайтеся поданими нижче функціями розширення

  1. Припускаючи, що ви хочете отримати лише останнє значення запиту, тобто 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. Якщо припустити, що ви хочете отримати список усіх значень для запиту, тобто 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}

1

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

0

Відповідаючи тут, бо це популярна тема. Це чистий розчин у Котліні, який використовує рекомендовані UrlQuerySanitizerapi. Дивіться офіційну документацію . Я додав конструктор струн для об'єднання та відображення парам.

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

0

Ось моє рішення зі скороченням та Необов’язково :

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

Якщо ви запитаєте, для зменшення потрібен цей дивний комбінатор в останньому параметрі, який використовується лише в паралельних потоках. Його мета - об'єднання двох проміжних результатів (тут HashMap).


-1

Якщо ви використовуєте Spring, додайте аргумент типу @RequestParam Map<String,String> до вашого методу контролера, і Spring створить для вас карту!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.