Разбор строки URI в коллекцию имя-значение
У меня есть URI вот так:
Мне нужна коллекция с разбираемые элементы:
NAME VALUE
------------------------
client_id SS
response_type code
scope N_FULL
access_type offline
redirect_uri http://localhost/Callback
чтобы быть точным, мне нужен эквивалент Java для C# HttpUtility.Метод ParseQueryString. Пожалуйста, дайте мне совет. Спасибо.
13 ответов:
если вы ищете способ добиться этого без использования внешней библиотеки, следующий код поможет вам.
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¶m2=¶m3=value3¶m3
возвращает эту карту:
{param1=["value1"], param2=[null], param3=["value3", null]}
орг.апаш.http.клиент.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
Если вы используете Spring Framework:
public static void main(String[] args) { String uri = "http://youhost.com/test?param1=abc¶m2=def¶m2=ghi"; 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: abc param2: def,ghi
используйте 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}
самый короткий путь я нашел это:
MultiValueMap<String, String> queryParams = UriComponentsBuilder.fromUriString(url).build().getQueryParams();
обновление:
UriComponentsBuilder
идет с весны. Здесь ссылке.
Если вы используете 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); } }
но это довольно жестокая линия.
для Android, Если вы используете OkHttp в вашем проекте. Вы можете взглянуть на это. Это просто и полезно.
final HttpUrl url = HttpUrl.parse(query); if (url != null) { final String target = url.queryParameter("target"); final String id = url.queryParameter("id"); }
Java 8 one statement
учитывая 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); } }
Если вы используете сервлет doGet попробуйте это
request.getParameterMap()
возвращает java.утиль.Карта параметров этого запроса.
возвращает: неизменяемая java.утиль.Карта, содержащая имена параметров в качестве ключей и значения параметров в качестве значений карты. Ключи в карте параметров имеют тип String. Значения в карте параметров имеют тип String array.
используя вышеупомянутые комментарии и решения, я сохраняю все параметры запроса, используя Map
, где объекты могут быть строкой или Set . Решение приведено ниже. Рекомендуется использовать какой-то валидатор 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; } }
просто обновление до версии 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 () должны использоваться с коллекторами, которые не были упомянуты в верхнем ответе. В противном случае это вызовет ошибку компиляции в IDE
Если вы используете Spring, добавьте аргумент типа
@RequestParam Map<String,String>
к вашему методу контроллера, и весна построит карту для вас!
Я пошел на версию 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") }