java - programacion - Cómo convertir el mapa a la cadena de consulta url?
manual de programacion android pdf (10)
¿Conoce alguna clase de utilidad / biblioteca que pueda convertir Map en una cadena de consulta amigable para las URL?
Ejemplo:
Tengo un mapa:
- "param1" = 12,
- "param2" = "cat"
Quiero obtener: param1=12¶m2=cat
.
PD. Sé que puedo escribirlo fácilmente, estoy sorprendido de no poder encontrarlo en ningún lado (revisé Apache Commons hasta ahora).
Aquí hay algo que escribí rápidamente; Estoy seguro de que se puede mejorar.
import java.util.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class MapQuery {
static String urlEncodeUTF8(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
static String urlEncodeUTF8(Map<?,?> map) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<?,?> entry : map.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(String.format("%s=%s",
urlEncodeUTF8(entry.getKey().toString()),
urlEncodeUTF8(entry.getValue().toString())
));
}
return sb.toString();
}
public static void main(String[] args) {
Map<String,Object> map = new HashMap<String,Object>();
map.put("p1", 12);
map.put("p2", "cat");
map.put("p3", "a & b");
System.out.println(urlEncodeUTF8(map));
// prints "p3=a+%26+b&p2=cat&p1=12"
}
}
El más robusto que vi fuera de la plataforma es la clase URLEncodedUtils de Apache Http Compoments (HttpClient 4.0).
El método URLEncodedUtils.format()
es lo que necesita.
No usa el mapa para que pueda tener nombres de parámetros duplicados, como
a=1&a=2&b=3
No es que recomiende este tipo de uso de nombres de parámetros.
En Spring Util, hay una mejor manera ..,
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("key", key);
params.add("storeId", storeId);
params.add("orderId", orderId);
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("http://spsenthil.com/order").queryParams(params).build();
ListenableFuture<ResponseEntity<String>> responseFuture = restTemplate.getForEntity(uriComponents.toUriString(), String.class);
Encontré una solución sin problemas usando java 8 y la solución de polygenelubricants ''.
parameters.entrySet().stream()
.map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue()))
.reduce((p1, p2) -> p1 + "&" + p2)
.orElse("");
Esta es la solución que implementé, usando Java 8 y org.apache.http.client.URLEncodedUtils
. BasicNameValuePair
las entradas del mapa en una lista de BasicNameValuePair
y luego utiliza URLEncodedUtils
de Apache para convertirlo en una cadena de consulta.
List<BasicNameValuePair> nameValuePairs = params.entrySet().stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
URLEncodedUtils.format(nameValuePairs, Charset.forName("UTF-8"));
Otra forma de ''una clase'' / sin dependencia de hacerlo, manejando solo / múltiple:
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class UrlQueryString {
private static final String DEFAULT_ENCODING = "UTF-8";
public static String buildQueryString(final LinkedHashMap<String, Object> map) {
try {
final Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
final StringBuilder sb = new StringBuilder(map.size() * 8);
while (it.hasNext()) {
final Map.Entry<String, Object> entry = it.next();
final String key = entry.getKey();
if (key != null) {
sb.append(URLEncoder.encode(key, DEFAULT_ENCODING));
sb.append(''='');
final Object value = entry.getValue();
final String valueAsString = value != null ? URLEncoder.encode(value.toString(), DEFAULT_ENCODING) : "";
sb.append(valueAsString);
if (it.hasNext()) {
sb.append(''&'');
}
} else {
// Do what you want...for example:
assert false : String.format("Null key in query map: %s", map.entrySet());
}
}
return sb.toString();
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
public static String buildQueryStringMulti(final LinkedHashMap<String, List<Object>> map) {
try {
final StringBuilder sb = new StringBuilder(map.size() * 8);
for (final Iterator<Entry<String, List<Object>>> mapIterator = map.entrySet().iterator(); mapIterator.hasNext();) {
final Entry<String, List<Object>> entry = mapIterator.next();
final String key = entry.getKey();
if (key != null) {
final String keyEncoded = URLEncoder.encode(key, DEFAULT_ENCODING);
final List<Object> values = entry.getValue();
sb.append(keyEncoded);
sb.append(''='');
if (values != null) {
for (final Iterator<Object> listIt = values.iterator(); listIt.hasNext();) {
final Object valueObject = listIt.next();
sb.append(valueObject != null ? URLEncoder.encode(valueObject.toString(), DEFAULT_ENCODING) : "");
if (listIt.hasNext()) {
sb.append(''&'');
sb.append(keyEncoded);
sb.append(''='');
}
}
}
if (mapIterator.hasNext()) {
sb.append(''&'');
}
} else {
// Do what you want...for example:
assert false : String.format("Null key in query map: %s", map.entrySet());
}
}
return sb.toString();
} catch (final UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
public static void main(final String[] args) {
// Examples:
{
final LinkedHashMap<String, Object> queryItems = new LinkedHashMap<String, Object>();
queryItems.put("brand", "C&A");
queryItems.put("count", null);
queryItems.put("misc", 42);
final String buildQueryString = buildQueryString(queryItems);
System.out.println(buildQueryString);
}
{
final LinkedHashMap<String, List<Object>> queryItems = new LinkedHashMap<String, List<Object>>();
queryItems.put("usernames", new ArrayList<Object>(Arrays.asList(new String[] { "bob", "john" })));
queryItems.put("nullValue", null);
queryItems.put("misc", new ArrayList<Object>(Arrays.asList(new Integer[] { 1, 2, 3 })));
final String buildQueryString = buildQueryStringMulti(queryItems);
System.out.println(buildQueryString);
}
}
}
Puede usar ya sea simple (más fácil de escribir en la mayoría de los casos) o múltiple cuando sea necesario. Tenga en cuenta que ambos se pueden combinar agregando un signo & ... Si encuentra algún problema, hágamelo saber en los comentarios.
Para mejorar un poco con la respuesta de @ eclipse: en Javaland, un mapa de parámetros de solicitud generalmente se representa como un Map<String, String[]>
, un Map<String, List<String>>
o posiblemente algún tipo de MultiValueMap<String, String>
que es una especie de la misma cosa. En cualquier caso: un parámetro generalmente puede tener múltiples valores. Una solución Java 8 sería, por lo tanto, algo similar a esta:
public String getQueryString(HttpServletRequest request, String encoding) {
Map<String, String[]> parameters = request.getParameterMap();
return parameters.entrySet().stream()
.flatMap(entry -> encodeMultiParameter(entry.getKey(), entry.getValue(), encoding))
.reduce((param1, param2) -> param1 + "&" + param2)
.orElse("");
}
private Stream<String> encodeMultiParameter(String key, String[] values, String encoding) {
return Stream.of(values).map(value -> encodeSingleParameter(key, value, encoding));
}
private String encodeSingleParameter(String key, String value, String encoding) {
return urlEncode(key, encoding) + "=" + urlEncode(value, encoding);
}
private String urlEncode(String value, String encoding) {
try {
return URLEncoder.encode(value, encoding);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Cannot url encode " + value, e);
}
}
Quería basarme en la respuesta de @ eclipse usando el mapeo y reducción java 8.
protected String formatQueryParams(Map<String, String> params) {
return params.entrySet().stream()
.map(p -> p.getKey() + "=" + p.getValue())
.reduce((p1, p2) -> p1 + "&" + p2)
.map(s -> "?" + s)
.orElse("");
}
La operación de map
extra toma la cadena reducida y pone un ?
al frente solo si la cadena existe.
Si realmente desea construir un URI completo, intente con URIBuilder desde Apache Http Compoments (HttpClient 4).
Esto en realidad no responde la pregunta, pero respondió la que tenía cuando encontré esta pregunta.
Actualización junio de 2016
Me sentí obligado a agregar una respuesta al haber visto demasiadas respuestas SOF con respuestas anticuadas o inadecuadas a un problema muy común: una buena biblioteca y un uso sólido de ejemplos para las operaciones de parse
y format
.
Use la biblioteca org.apache.httpcomponents.httpclient . La biblioteca contiene esta utilidad de clase org.apache.http.client.utils.URLEncodedUtils .
Por ejemplo, es fácil descargar esta dependencia de Maven:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
Para mis propósitos, solo necesitaba parse
(leer de cadena de consulta a pares de nombre-valor) y format
(leer de pares de nombre-valor para consultar cadena) cadenas de consulta. Sin embargo, hay equivalentes para hacer lo mismo con un URI (ver la línea comentada a continuación).
// Importaciones requeridas
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// fragmento de código
public static void parseAndFormatExample() throws UnsupportedEncodingException {
final String queryString = "nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk";
System.out.println(queryString);
// => nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk
final List<NameValuePair> params =
URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8);
// List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), "UTF-8");
for (final NameValuePair param : params) {
System.out.println(param.getName() + " : " + param.getValue());
// => nonce : 12345
// => redirectCallbackUrl : http://www.bbc.co.uk
}
final String newQueryStringEncoded =
URLEncodedUtils.format(params, StandardCharsets.UTF_8);
// decode when printing to screen
final String newQueryStringDecoded =
URLDecoder.decode(newQueryStringEncoded, StandardCharsets.UTF_8.toString());
System.out.println(newQueryStringDecoded);
// => nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk
}
Esta biblioteca hizo exactamente lo que necesitaba y fue capaz de reemplazar algún código personalizado pirateado.