Analisar uma string de URI na coleção de valor de nome

Eu tenho o URI assim:

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

Eu preciso de uma coleção com elementos analisados:

NAME VALUE ------------------------ client_id SS response_type code scope N_FULL access_type offline redirect_uri http://localhost/Callback 

Para ser exato, preciso de um equivalente Java para o método HttpUtility.ParseQueryString do C #. Por favor, me dê um conselho sobre isso. Obrigado.

Se você está procurando uma maneira de alcançá-lo sem usar uma biblioteca externa, o código a seguir irá ajudá-lo.

 public static Map splitQuery(URL url) throws UnsupportedEncodingException { Map query_pairs = new LinkedHashMap(); 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; } 

Você pode acessar o Mapa retornado usando

.get("client_id")

, com o URL indicado em sua pergunta, isso retornaria “SS”.

UPDATE URL-Decoding added

UPDATE Como esta resposta ainda é bastante popular, eu fiz uma versão melhorada do método acima, que lida com vários parâmetros com a mesma chave e parâmetros sem valor também.

 public static Map> splitQuery(URL url) throws UnsupportedEncodingException { final Map> query_pairs = new LinkedHashMap>(); 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()); } 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; } 

ATUALIZAÇÃO da versão Java8

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

Executando o método acima com o URL

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

retorna este mapa:

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

org.apache.http.client.utils.URLEncodedUtils

é uma biblioteca bem conhecida que pode fazer isso por você

 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 params = URLEncodedUtils.parse(new URI(url), Charset.forName("UTF-8")); for (NameValuePair param : params) { System.out.println(param.getName() + " : " + param.getValue()); } 

Saídas

 one : 1 two : 2 three : 3 three : 3a 

Se você estiver usando o Spring Framework:

 public static void main(String[] args) { String uri = "http://youhost.com/test?param1=abc&param2=def&param2=ghi"; MultiValueMap parameters = UriComponentsBuilder.fromUriString(uri).build().getQueryParams(); List param1 = parameters.get("param1"); List param2 = parameters.get("param2"); System.out.println("param1: " + param1.get(0)); System.out.println("param2: " + param2.get(0) + "," + param2.get(1)); } 

Você vai ter:

 param1: abc param2: def,ghi 

use o Google goava e faça em 2 linhas:

 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 map = Splitter.on('&').trimResults().withKeyValueSeparator("=").split(query); System.out.println(map); } } 

o que te dá

 {client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback} 

O caminho mais curto que encontrei é este:

 MultiValueMap queryParams = UriComponentsBuilder.fromUriString(url).build().getQueryParams(); 

UPDATE: UriComponentsBuilder vem da spring. Aqui o link .

Se você estiver usando o Java 8 e estiver disposto a escrever alguns methods reutilizáveis, poderá fazê-lo em uma linha.

 private Map> 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  List mergeLists(final List l1, final List l2) { List list = new ArrayList<>(); list.addAll(l1); list.addAll(l2); return list; } private static  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); } } 

Mas essa é uma linha bastante brutal.

Java 8 uma declaração

Dado o URL para analisar:

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

Esta solução coleta uma lista de pares:

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

Esta solução, por outro lado, coleta um mapa (dado que em um url pode haver mais parâmetros com o mesmo nome, mas valores diferentes).

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

Ambas as soluções devem usar uma function de utilidade para decodificar corretamente os parâmetros.

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

Para Android, se você estiver usando o OkHttp no seu projeto. Você pode dar uma olhada nisso. É simples e útil.

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

Se você estiver usando o servlet doGet, tente isto

 request.getParameterMap() 

Retorna um java.util.Map dos parâmetros dessa solicitação.

Retorna: um java.util.Map imutável contendo nomes de parâmetros como chaves e valores de parâmetros como valores de mapa. As chaves no mapa de parâmetros são do tipo String. Os valores no mapa de parâmetros são do tipo String array.

( Doc de Java )

Usando os comentários e soluções mencionados acima, estou armazenando todos os parâmetros de consulta usando Map , em que Objects pode ser string ou Set <>. A solução é dada abaixo. Recomenda-se usar algum tipo de validador de URL para validar a URL primeiro e depois chamar o método convertQueryStringToMap.

 private static final String DEFAULT_ENCODING_SCHEME = "UTF-8"; public static Map convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException { List params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME); Map 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(Arrays.asList(value.split(","))) : value; } else { Set queryValueSet = responseMap.get(key) instanceof Set ? (Set) responseMap.get(key) : new HashSet(); if (value.contains(",")) { queryValueSet.addAll(Arrays.asList(value.split(","))); } else { queryValueSet.add(value); } return queryValueSet; } } 

Se você estiver usando o Spring, adicione um argumento do tipo @RequestParam Map ao seu método de controle, e o Spring irá construir o mapa para você!

Apenas uma atualização para a versão do Java 8

 public Map> 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()))); } 

Os methods mapping e toList () devem ser usados ​​com Collectors, o que não foi mencionado na resposta principal. Caso contrário, isso causaria erro de compilation no IDE

Eu tive uma chance em uma versão do Kotlin, vendo como esse é o melhor resultado no Google.

 @Throws(UnsupportedEncodingException::class) fun splitQuery(url: URL): Map> { val queryPairs = LinkedHashMap>() 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 } 

E os methods de extensão

 fun List.getOrEmpty(index: Int) : String { return getOrElse(index) {""} } fun String.decodeToUTF8(): String { URLDecoder.decode(this, "UTF-8") }