Analizar una cadena de URI en la colección nombre-valor

Tengo el URI así:

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

Necesito una colección con elementos analizados:

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

Para ser exactos, necesito un equivalente Java para el método C # HttpUtility.ParseQueryString. Por favor, dame un consejo sobre esto. Gracias.

Si busca una forma de lograrlo sin utilizar una biblioteca externa, el siguiente código lo ayudará.

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

Puede acceder al Mapa devuelto utilizando

.get("client_id")

, con la URL dada en su pregunta, esto devolvería “SS”.

UPDATE URL-Deencoding agregada

ACTUALIZACIÓN Como esta respuesta todavía es bastante popular, hice una versión mejorada del método anterior, que maneja múltiples parámetros con la misma clave y parámetros sin ningún valor.

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

ACTUALIZAR la versión de 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); } 

Ejecutando el método anterior con la URL

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

devuelve este mapa:

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

org.apache.http.client.utils.URLEncodedUtils

es una biblioteca muy conocida que puede hacerlo por ti

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

Salidas

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

Si está utilizando 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)); } 

Conseguirás:

 param1: abc param2: def,ghi 

usa Google Guava y hazlo en 2 líneas:

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

que te da

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

La forma más corta que he encontrado es esta:

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

ACTUALIZACIÓN: UriComponentsBuilder viene de Spring. Aquí el enlace .

Si está utilizando Java 8 y está dispuesto a escribir algunos métodos reutilizables, puede hacerlo en una línea.

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

Pero esa es una línea bastante brutal.

Java 8 una statement

Dada la URL para analizar:

 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 solución recostack una 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 solución, por otro lado, recostack un mapa (dado que en una url puede haber más parámetros con el mismo nombre pero diferentes valores).

 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 soluciones deben usar una función de utilidad para decodificar correctamente los 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, si está utilizando OkHttp en su proyecto. Puede echar un vistazo a esto. Es simple y útil.

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

Si está utilizando servlet doget, intente esto

 request.getParameterMap() 

Devuelve un java.util.Mapa de los parámetros de esta solicitud.

Devuelve: un java.util.Map inmutable que contiene nombres de parámetros como claves y valores de parámetros como valores de mapa. Las claves en el mapa de parámetros son de tipo String. Los valores en el mapa de parámetros son del tipo String array.

( Java doc )

Usando los comentarios y soluciones mencionados anteriormente, estoy almacenando todos los parámetros de consulta utilizando Map donde Objects puede ser string o Set . La solución se da a continuación. Se recomienda utilizar algún tipo de validador de URL para validar la url primero y luego llamar al 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; } } 

Si está utilizando Spring, agregue un argumento de tipo @RequestParam Map a su método de controlador, ¡y Spring construirá el mapa para usted!

Solo una actualización de la versión de 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()))); } 

los métodos de mapeo y toList () deben ser usados ​​con los recolectores que no se mencionaron en la respuesta superior. De lo contrario arrojaría un error de comstackción en IDE

He probado una versión de Kotlin viendo cómo este es el resultado principal en 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 } 

Y los métodos de extensión

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