Análisis de cadenas de consulta en Android

Java EE tiene ServletRequest.getParameterValues ​​() .

En plataformas no EE, URL.getQuery () simplemente devuelve una cadena.

¿Cuál es la forma normal de analizar correctamente la cadena de consulta en una URL cuando no está en Java EE?


< Rant >

Es popular en las respuestas para tratar de hacer su propio analizador. Esto es muy interesante y emocionante proyecto de micro-codificación, pero no puedo decir que es una buena idea 🙁

Los fragmentos de código a continuación son generalmente defectuosos o rotos, por cierto. Romperlos es un ejercicio interesante para el lector. Y a los hackers que atacan los sitios web que los utilizan .

Analizar las cadenas de consulta es un problema bien definido, pero leer las especificaciones y comprender los matices no es trivial. Es mucho mejor dejar que algún codificador de biblioteca de plataforma haga el trabajo duro, y hacer la fijación, para usted!

< / Rant >

Desde Android M las cosas se han complicado más. La respuesta de android.net.URI .getQueryParameter () tiene un error que rompe espacios antes de JellyBean. Apache URLEncodedUtils.parse () funcionó, pero se desaconsejó en L y se eliminó en M.

Así que la mejor respuesta ahora es UrlQuerySanitizer . Esto ha existido desde el nivel 1 de API y aún existe. También te hace pensar en los problemas difíciles como cómo manejas caracteres especiales o valores repetidos.

El código más simple es

 UrlQuerySanitizer.ValueSanitizer sanitizer = UrlQuerySanitizer.getAllButNullLegal(); // remember to decide if you want the first or last parameter with the same name // If you want the first call setPreferFirstRepeatedParameter(true); sanitizer.parseUrl(url); String value = sanitizer.getValue("paramname"); // get your value 

En Android:

 import android.net.Uri; [...] Uri uri=Uri.parse(url_string); uri.getQueryParameter("para1"); 

En Android, las bibliotecas Apache proporcionan un analizador de consultas:

http://developer.android.com/reference/org/apache/http/client/utils/URLEncodedUtils.html y http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/ Http / client / utils / URLEncodedUtils.html

Aquí está la respuesta de BalusC , pero compila y devuelve resultados:

 public static Map<String, List<String>> getUrlParameters(String url) throws UnsupportedEncodingException { Map<String, List<String>> params = new HashMap<String, List<String>>(); String[] urlParts = url.split("\\?"); if (urlParts.length > 1) { String query = urlParts[1]; for (String param : query.split("&")) { String pair[] = param.split("="); String key = URLDecoder.decode(pair[0], "UTF-8"); String value = ""; if (pair.length > 1) { value = URLDecoder.decode(pair[1], "UTF-8"); } List<String> values = params.get(key); if (values == null) { values = new ArrayList<String>(); params.put(key, values); } values.add(value); } } return params; } 

Si tiene bibliotecas jetty (servidor o cliente) en su classpath puede utilizar las clases util jetty (ver javadoc ), por ejemplo:

 import org.eclipse.jetty.util.*; URL url = new URL("www.example.com/index.php?foo=bar&bla=blub"); MultiMap<String> params = new MultiMap<String>(); UrlEncoded.decodeTo(url.getQuery(), params, "UTF-8"); assert params.getString("foo").equals("bar"); assert params.getString("bla").equals("blub"); 

Si utiliza Spring 3.1 o superior (yikes, esperaba que la compatibilidad retrocediera más), puede utilizar UriComponents y UriComponentsBuilder :

 UriComponents components = UriComponentsBuilder.fromUri(uri).build(); List<String> myParam = components.getQueryParams().get("myParam"); 

components.getQueryParams() devuelve un MultiValueMap<String, String>

Aquí hay más documentación .

Para un servlet o una página JSP, puede obtener pares de clave / valor querystring usando request.getParameter ("paramname")

 String name = request.getParameter("name"); 

Hay otras maneras de hacerlo, pero esa es la forma en que lo hago en todos los servlets y páginas jsp que creo.

Parsing la cadena de consulta es un poco más complicado de lo que parece, dependiendo de cómo perdonar quieres ser.

En primer lugar, la cadena de consulta es ascii bytes. Usted lee estos bytes uno a la vez y los convierte en caracteres. Si el personaje es? O & luego señala el inicio de un nombre de parámetro. Si el carácter es = entonces señala el inicio de un valor de parámetro. Si el carácter es% entonces señala el inicio de un byte codificado. Aquí es donde se pone difícil.

Cuando lee en un% char, tiene que leer los dos bytes siguientes e interpretarlos como dígitos hexadecimales. Esto significa que los próximos dos bytes serán 0-9, af o AF. Pegue estos dos dígitos hexadecimales juntos para obtener su valor de byte. Pero recuerde, los bytes no son caracteres . Tienes que saber qué codificación se utilizó para codificar los caracteres. El carácter é no codifica lo mismo en UTF-8 como lo hace en ISO-8859-1. En general, es imposible saber qué codificación se utilizó para un determinado conjunto de caracteres. Siempre uso UTF-8 porque mi sitio web está configurado para servir siempre todo usando UTF-8 pero en la práctica no puedes estar seguro. Algunos agentes de usuario le dirán la codificación de caracteres en la solicitud; Puede intentar leer que si tiene una solicitud HTTP completa. Si sólo tiene una url en aislamiento, buena suerte.

De todos modos, suponiendo que esté utilizando UTF-8 u otra codificación de caracteres de varios bytes, ahora que ha decodificado un byte codificado, tiene que dejarlo a un lado hasta capturar el siguiente byte. Necesita todos los bytes codificados que están juntos porque no puede url-decodificar correctamente un byte a la vez. Deje de lado todos los bytes que están juntos y luego decodificarlos todos a la vez para reconstruir su personaje.

Además, se vuelve más divertido si quieres ser indulgente y tener en cuenta a los agentes de usuarios que mangle urls. Por ejemplo, algunos clientes de correo web codifican dos cosas. O doblar el? & = Chars (por ejemplo: http://yoursite.com/blah??p1==v1&&p2==v2 ). Si desea tratar con gracia tratar con esto, tendrá que agregar más lógica a su analizador.

Sólo para referencia, esto es lo que he terminado con (basado en URLEncodedUtils, y devolver un Mapa).

caracteristicas:

  • Acepta la parte de la cadena de consulta de la url (puede utilizar request.getQueryString() )
  • Una cadena de consulta vacía producirá un Map vacío
  • Un parámetro sin un valor (? Test) se asignará a una List<String> vacía List<String>

Código:

 public static Map<String, List<String>> getParameterMapOfLists(String queryString) { Map<String, List<String>> mapOfLists = new HashMap<String, List<String>>(); if (queryString == null || queryString.length() == 0) { return mapOfLists; } List<NameValuePair> list = URLEncodedUtils.parse(URI.create("http://localhost/?" + queryString), "UTF-8"); for (NameValuePair pair : list) { List<String> values = mapOfLists.get(pair.getName()); if (values == null) { values = new ArrayList<String>(); mapOfLists.put(pair.getName(), values); } if (pair.getValue() != null) { values.add(pair.getValue()); } } return mapOfLists; } 

Un ayudante de compatibilidad (los valores se almacenan en una matriz String como en ServletRequest.getParameterMap () ):

 public static Map<String, String[]> getParameterMap(String queryString) { Map<String, List<String>> mapOfLists = getParameterMapOfLists(queryString); Map<String, String[]> mapOfArrays = new HashMap<String, String[]>(); for (String key : mapOfLists.keySet()) { mapOfArrays.put(key, mapOfLists.get(key).toArray(new String[] {})); } return mapOfArrays; } 

En Android, intenté usar la respuesta de @diyism pero encontré el problema de carácter de espacio planteado por @rpetrich, por ejemplo: relleno un formulario donde username = "us+us" y password = "pw pw" causando una cadena de URL a buscar me gusta:

 http://somewhere?username=us%2Bus&password=pw+pw 

Sin embargo, el código @diyism devuelve "us+us" y "pw+pw" , es decir, no detecta el carácter de espacio. Si la URL se reescribió con %20 el carácter de espacio se identifica:

 http://somewhere?username=us%2Bus&password=pw%20pw 

Esto lleva a la siguiente revisión:

 Uri uri = Uri.parse(url_string.replace("+", "%20")); uri.getQueryParameter("para1"); 

Tengo que los métodos para acheve esto:

1) :

 public static String getQueryString(String url, String tag) { String[] params = url.split("&"); Map<String, String> map = new HashMap<String, String>(); for (String param : params) { String name = param.split("=")[0]; String value = param.split("=")[1]; map.put(name, value); } Set<String> keys = map.keySet(); for (String key : keys) { if(key.equals(tag)){ return map.get(key); } System.out.println("Name=" + key); System.out.println("Value=" + map.get(key)); } return ""; } 

2) y la forma más sencilla de hacerlo Usando la clase Uri :

 public static String getQueryString(String url, String tag) { try { Uri uri=Uri.parse(url); return uri.getQueryParameter(tag); }catch(Exception e){ Log.e(TAG,"getQueryString() " + e.getMessage()); } return ""; } 

Y este es un ejemplo de cómo utilizar cualquiera de dos métodos:

 String url = "http://www.jorgesys.com/advertisements/publicidadmobile.htm?position=x46&site=reform&awidth=800&aheight=120"; String tagValue = getQueryString(url,"awidth"); 

El valor de tagValue es 800

Esto funciona para mí .. No estoy seguro de por qué cada uno fue después de un Mapa, Lista> Todo lo que necesitaba era un mapa de nombre simple valor.

Para mantener las cosas simples, utilicé la construcción en URI.getQuery ();

 public static Map<String, String> getUrlParameters(URI uri) throws UnsupportedEncodingException { Map<String, String> params = new HashMap<String, String>(); for (String param : uri.getQuery().split("&")) { String pair[] = param.split("="); String key = URLDecoder.decode(pair[0], "UTF-8"); String value = ""; if (pair.length > 1) { value = URLDecoder.decode(pair[1], "UTF-8"); } params.put(new String(key), new String(value)); } return params; } 

En Android, puede utilizar el método estático Uri.parse de la clase android.net.Uri para realizar el trabajo pesado. Si usted está haciendo cualquier cosa con URIs y Intents usted querrá utilizarlo anyways.

El Multimap de la guayaba es más adecuado para esto. Esta es una versión corta y limpia:

 Multimap<String, String> getUrlParameters(String url) { try { Multimap<String, String> ret = ArrayListMultimap.create(); for (NameValuePair param : URLEncodedUtils.parse(new URI(url), "UTF-8")) { ret.put(param.getName(), param.getValue()); } return ret; } catch (URISyntaxException e) { throw new RuntimeException(e); } } 

En Android es simple como el código de abajo:

 UrlQuerySanitizer sanitzer = new UrlQuerySanitizer(url); String value = sanitzer.getValue("your_get_parameter"); 

Además, si no desea registrar cada clave de consulta esperada, utilice:

 sanitzer.setAllowUnregisteredParamaters(true) 

Antes de llamar a:

 sanitzer.parseUrl(yourUrl) 

No creo que haya uno en JRE. Puede encontrar funciones similares en otros paquetes como Apache HttpClient. Si no usas ningún otro paquete, solo tienes que escribir el tuyo. No es tan dificil. Aquí es lo que uso,

 public class QueryString { private Map<String, List<String>> parameters; public QueryString(String qs) { parameters = new TreeMap<String, List<String>>(); // Parse query string String pairs[] = qs.split("&"); for (String pair : pairs) { String name; String value; int pos = pair.indexOf('='); // for "n=", the value is "", for "n", the value is null if (pos == -1) { name = pair; value = null; } else { try { name = URLDecoder.decode(pair.substring(0, pos), "UTF-8"); value = URLDecoder.decode(pair.substring(pos+1, pair.length()), "UTF-8"); } catch (UnsupportedEncodingException e) { // Not really possible, throw unchecked throw new IllegalStateException("No UTF-8"); } } List<String> list = parameters.get(name); if (list == null) { list = new ArrayList<String>(); parameters.put(name, list); } list.add(value); } } public String getParameter(String name) { List<String> values = parameters.get(name); if (values == null) return null; if (values.size() == 0) return ""; return values.get(0); } public String[] getParameterValues(String name) { List<String> values = parameters.get(name); if (values == null) return null; return (String[])values.toArray(new String[values.size()]); } public Enumeration<String> getParameterNames() { return Collections.enumeration(parameters.keySet()); } public Map<String, String[]> getParameterMap() { Map<String, String[]> map = new TreeMap<String, String[]>(); for (Map.Entry<String, List<String>> entry : parameters.entrySet()) { List<String> list = entry.getValue(); String[] values; if (list == null) values = null; else values = (String[]) list.toArray(new String[list.size()]); map.put(entry.getKey(), values); } return map; } } 

Basado en la respuesta de BalusC, escribí algunos ejemplos-Java-Code:

  if (queryString != null) { final String[] arrParameters = queryString.split("&"); for (final String tempParameterString : arrParameters) { final String[] arrTempParameter = tempParameterString.split("="); if (arrTempParameter.length >= 2) { final String parameterKey = arrTempParameter[0]; final String parameterValue = arrTempParameter[1]; //do something with the parameters } } } 
 public static Map <String, String> parseQueryString (final URL url) throws UnsupportedEncodingException { final Map <String, String> qps = new TreeMap <String, String> (); final StringTokenizer pairs = new StringTokenizer (url.getQuery (), "&"); while (pairs.hasMoreTokens ()) { final String pair = pairs.nextToken (); final StringTokenizer parts = new StringTokenizer (pair, "="); final String name = URLDecoder.decode (parts.nextToken (), "ISO-8859-1"); final String value = URLDecoder.decode (parts.nextToken (), "ISO-8859-1"); qps.put (name, value); } return qps; } 

Utilizar Apache HttpComponents y conectarlo con algún código de colección para acceder a los parámetros por valor: http://www.joelgerard.com/2012/09/14/parsing-query-strings-in-java-and-accessing-values-by -llave/

Usando la guayaba:

 Multimap<String,String> parseQueryString(String queryString, String encoding) { LinkedListMultimap<String, String> result = LinkedListMultimap.create(); for(String entry : Splitter.on("&").omitEmptyStrings().split(queryString)) { String pair [] = entry.split("=", 2); try { result.put(URLDecoder.decode(pair[0], encoding), pair.length == 2 ? URLDecoder.decode(pair[1], encoding) : null); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } return result; } 

Este método toma el uri y devuelve el mapa de par nombre y valor nominal

  public static Map<String, String> getQueryMap(String uri) { String queryParms[] = uri.split("\\?"); Map<String, String> map = new HashMap<>();// if (queryParms == null || queryParms.length == 0) return map; String[] params = queryParms[1].split("&"); for (String param : params) { String name = param.split("=")[0]; String value = param.split("=")[1]; map.put(name, value); } return map; } 

Usted dice "Java" pero "no Java EE". ¿Quiere decir que está utilizando JSP y / o servlets pero no una pila completa de Java EE? Si ese es el caso, entonces usted todavía debe tener request.getParameter () a su disposición.

Si quiere decir que está escribiendo Java pero no está escribiendo JSPs ni servlets, o que está usando Java como su punto de referencia, pero está en alguna otra plataforma que no tiene parsing de parámetros incorporados … Wow , Eso suena como una pregunta improbable, pero si es así, el principio sería:

 xparm=0 word="" loop get next char if no char exit loop if char=='=' param_name[xparm]=word word="" else if char=='&' param_value[xparm]=word word="" xparm=xparm+1 else if char=='%' read next two chars word=word+interpret the chars as hex digits to make a byte else word=word+char 

(Yo podría escribir código Java, pero que sería inútil, porque si tiene Java disponible, sólo puede utilizar request.getParameters.)

  • Esquema personalizado en el texto SMS
  • ¿Es posible mostrar una miniatura de vídeo desde una URL en Android 4 o superior?
  • ¿Cómo funciona Twitter OAuth en Android?
  • ¿Cómo puedo descifrar la URL en la aplicación Android
  • Obtenga la colección de nombres de parámetros de la url de Java / Android
  • Android: establece ImageView en la URL
  • Java, android, resolver un url, obtener redireccionado uri
  • Android: longitud máxima de url
  • Problema al extraer el número de puerto con URL.getPort () cuando la URL contiene "]"
  • Establecer tiempo de espera en URL.openStream () Android
  • ¿Cómo puedo iniciar mi aplicación a través de una URL personalizada en un correo electrónico
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.