2009-03-11 14 views
7

Tengo un Mapa con mis datos y quiero construir una cadena de consulta con él, al igual que lo haría con http_build_query en PHP. No estoy seguro de si este código es la mejor implementación o si me olvido de algo.¿Hay algún equivalente Java de la función http_build_query de PHP?

public String toQueryString(Map<?, ?> data) throws UnsupportedEncodingException { 
    StringBuffer queryString = new StringBuffer(); 

    for (Entry<?, ?> pair : data.entrySet()) { 
     queryString.append (URLEncoder.encode ((String) pair.getKey(), "UTF-8") + "="); 
     queryString.append (URLEncoder.encode ((String) pair.getValue(), "UTF-8") + "&"); 
    } 

    if (queryString.length() > 0) { 
     queryString.deleteCharAt (queryString.length() - 1); 
    } 

    return queryString.toString(); 
} 
+0

¿Está funcionando su código? ¿Dónde está el problema? ¿Cuál es la pregunta? –

+0

Mi código funciona. Mi pregunta era si hay alguna clase o método que hace esto de una mejor manera. No me gusta reinventar la rueda si hay algo listo para eso que pueda tomar un mejor enfoque en el caso que mi código. – falmp

Respuesta

6

vistazo a la clase QueryStringBuilder y su test class:

private String httpBuildQuery(Map<String, String> data) 
     throws UnsupportedEncodingException { 
    QueryStringBuilder builder = new QueryStringBuilder(); 
    for (Entry<String, String> pair : data.entrySet()) { 
     builder.addQueryParameter(pair.getKey(), pair.getValue()); 
    } 
    return builder.encode("UTF-8"); 
} 
+0

Estoy aceptando esta respuesta porque eso es exactamente lo que pregunté. Sin embargo, me quedo con mi propia versión ya que estaba bastante cerca de eso. :) – falmp

+0

Se ve bien, debe considerar empaquetarlo o enviarlo como una biblioteca. Una pequeña objeción, el nombre es engañoso: no se trata simplemente de crear una cadena de consulta, sino que también incluye una ruta. – pimlottc

+0

Este es un enlace roto ahora. – Flimm

0

se ve bien, con estas salvedades:

  • hacen que el parámetro a Map<String, String> en lugar de fundición clave y el valor de cadena.
  • la codificación codificada parece sospechosa. UTF-8 no es un hecho, tiene que coincidir con la codificación definida en el encabezado de la solicitud HTTP. Por lo tanto, el código debe garantizar que lo hagan, al menos definirlo como una constante en alguna parte y referirse a eso tanto aquí como donde se establezca la codificación de la solicitud.

Editar: Parece que estaba equivocado sobre la codificación; Los parámetros HTTP GET no están sujetos a un encabezado de codificación, y tradicionalmente no tienen una codificación bien definida. RFC 3988 parece obligar a UTF-8, pero eso suena bastante frágil para mí, así que a menos que tengas un control estricto sobre el servidor y puedas asegurarte de que sí usa UTF-8 también, usaría solicitudes POST para cualquier dato que sea no en el rango ASCII de 7 bits.

+0

a) Se arregló, gracias. b) Parece que tienes razón, pero estoy usando restlet.paquetes org para hacer una solicitud REST y no le permite definir la codificación de la solicitud. Digamos que no especifica uno, ¿cuál sería el valor predeterminado? Depende de la configuración del servidor? – falmp

1

Probablemente usted quiere comprobar el encabezado de la solicitud "Aceptar" para las codificaciones soportadas por el cliente antes de forzar UTF-8 (a pesar de que es probable que sea La mejor decision).

2

El verdadero poder de la función http_build_query de PHP es su capacidad para obtener una matriz asociativa y traducirla a la cadena URL. El siguiente código hace algo similar y permite que los parámetros de URL se construyan como un mapa de múltiples niveles que incluye mapas anidados y colecciones. Con algo más de trabajo, también se puede agregar un soporte para Array.

Los métodos de prueba se muestran a continuación.

import java.io.UnsupportedEncodingException; 
import java.net.URLEncoder; 
import java.util.*; 

/** 
* Class: URLBuilder 
* User: Gilad Tiram 
* Date: 6/12/13 
* Time: 4:02 PM 
* <p/> 
* <p/> 
* Utility that helps to build URL String 
*/ 
public class URLBuilder { 


/** 
* Build URL string from Map of params. Nested Map and Collection is also supported 
* 
* @param params Map of params for constructing the URL Query String 
* @param encoding encoding type. If not set the "UTF-8" is selected by default 
* @return String of type key=value&...key=value 
* @throws java.io.UnsupportedEncodingException 
*   if encoding isnot supported 
*/ 
public static String httpBuildQuery(Map<String, Object> params, String encoding) { 
    if (isEmpty(encoding)) { 
     encoding = "UTF-8"; 
    } 
    StringBuilder sb = new StringBuilder(); 
    for (Map.Entry<String, Object> entry : params.entrySet()) { 
     if (sb.length() > 0) { 
      sb.append('&'); 
     } 

     String name = entry.getKey(); 
     Object value = entry.getValue(); 


     if (value instanceof Map) { 
      List<String> baseParam = new ArrayList<String>(); 
      baseParam.add(name); 
      String str = buildUrlFromMap(baseParam, (Map) value, encoding); 
      sb.append(str); 

     } else if (value instanceof Collection) { 
      List<String> baseParam = new ArrayList<String>(); 
      baseParam.add(name); 
      String str = buildUrlFromCollection(baseParam, (Collection) value, encoding); 
      sb.append(str); 

     } else { 
      sb.append(encodeParam(name)); 
      sb.append("="); 
      sb.append(encodeParam(value)); 
     } 


    } 
    return sb.toString(); 
} 

private static String buildUrlFromMap(List<String> baseParam, Map<Object, Object> map, String encoding) { 
    StringBuilder sb = new StringBuilder(); 
    String token; 

    //Build string of first level - related with params of provided Map 
    for (Map.Entry<Object, Object> entry : map.entrySet()) { 

     if (sb.length() > 0) { 
      sb.append('&'); 
     } 

     String name = String.valueOf(entry.getKey()); 
     Object value = entry.getValue(); 
     if (value instanceof Map) { 
      List<String> baseParam2 = new ArrayList<String>(baseParam); 
      baseParam2.add(name); 
      String str = buildUrlFromMap(baseParam2, (Map) value, encoding); 
      sb.append(str); 

     } else if (value instanceof List) { 
      List<String> baseParam2 = new ArrayList<String>(baseParam); 
      baseParam2.add(name); 
      String str = buildUrlFromCollection(baseParam2, (List) value, encoding); 
      sb.append(str); 
     } else { 
      token = getBaseParamString(baseParam) + "[" + name + "]=" + encodeParam(value); 
      sb.append(token); 
     } 
    } 

    return sb.toString(); 
} 

private static String buildUrlFromCollection(List<String> baseParam, Collection coll, String encoding) { 
    StringBuilder sb = new StringBuilder(); 
    String token; 
    if (!(coll instanceof List)) { 
     coll = new ArrayList(coll); 
    } 
    List arrColl = (List) coll; 

    //Build string of first level - related with params of provided Map 
    for (int i = 0; i < arrColl.size(); i++) { 

     if (sb.length() > 0) { 
      sb.append('&'); 
     } 

     Object value = (Object) arrColl.get(i); 
     if (value instanceof Map) { 
      List<String> baseParam2 = new ArrayList<String>(baseParam); 
      baseParam2.add(String.valueOf(i)); 
      String str = buildUrlFromMap(baseParam2, (Map) value, encoding); 
      sb.append(str); 

     } else if (value instanceof List) { 
      List<String> baseParam2 = new ArrayList<String>(baseParam); 
      baseParam2.add(String.valueOf(i)); 
      String str = buildUrlFromCollection(baseParam2, (List) value, encoding); 
      sb.append(str); 
     } else { 
      token = getBaseParamString(baseParam) + "[" + i + "]=" + encodeParam(value); 
      sb.append(token); 
     } 
    } 

    return sb.toString(); 
} 


private static String getBaseParamString(List<String> baseParam) { 
    StringBuilder sb = new StringBuilder(); 
    for (int i = 0; i < baseParam.size(); i++) { 
     String s = baseParam.get(i); 
     if (i == 0) { 
      sb.append(s); 
     } else { 
      sb.append("[" + s + "]"); 
     } 
    } 
    return sb.toString(); 
} 

/** 
* Check if String is either empty or null 
* 
* @param str string to check 
* @return true if string is empty. Else return false 
*/ 
public static boolean isEmpty(String str) { 
    return str == null || str.length() == 0; 
} 


private static String encodeParam(Object param) { 
    try { 
     return URLEncoder.encode(String.valueOf(param), "UTF-8"); 
    } catch (UnsupportedEncodingException e) { 
     return URLEncoder.encode(String.valueOf(param)); 
    } 
} 

/* ========================================================================= */ 
/* Test functions               */ 
/* ========================================================================= */ 


public static void main(String[] args) { 
    //basicTest(); 
    //testWithMap(); 
    //testWithList(); 
    //testWithNestedMap(); 
    //testWithNestedList(); 
    testCompound(); 
} 

private static void basicTest() { 
    Map<String, Object> params = new LinkedHashMap<String, Object>(); 
    params.put("a", "1"); 
    params.put("b", "2"); 
    params.put("c", "3"); 

    System.out.println(httpBuildQuery(params, "UTF-8")); 
} 

private static void testWithMap() { 
    Map<String, Object> params = new LinkedHashMap<String, Object>(); 
    params.put("a", "1"); 
    params.put("b", "2"); 

    Map<String, Object> cParams = new LinkedHashMap<String, Object>(); 
    cParams.put("c1", "c1val"); 
    cParams.put("c2", "c2val"); 
    params.put("c", cParams); 

    System.out.println(httpBuildQuery(params, "UTF-8")); 
} 

private static void testWithNestedMap() { 
    Map<String, Object> params = new LinkedHashMap<String, Object>(); 
    params.put("a", "1"); 
    params.put("b", "2"); 

    Map<String, Object> cParamsLevel1 = new LinkedHashMap<String, Object>(); 
    cParamsLevel1.put("cL1-1", "cLevel1-1val"); 
    cParamsLevel1.put("cL1-2", "cLevel1-2val"); 

    Map<String, Object> cParamsLevel2 = new LinkedHashMap<String, Object>(); 
    cParamsLevel2.put("cL2-1", "cLevel2-1val"); 
    cParamsLevel2.put("cL2-2", "cLevel2-2val"); 
    cParamsLevel1.put("cL1-3", cParamsLevel2); 

    params.put("c", cParamsLevel1); 

    System.out.println(httpBuildQuery(params, "UTF-8")); 
} 


private static void testWithList() { 
    Map<String, Object> params = new LinkedHashMap<String, Object>(); 
    params.put("a", "1"); 
    params.put("b", "2"); 

    List<Object> cParams = new ArrayList<Object>(); 
    cParams.add("c1val"); 
    cParams.add("c2val"); 
    params.put("c", cParams); 

    System.out.println(httpBuildQuery(params, "UTF-8")); 
} 


private static void testWithNestedList() { 
    Map<String, Object> params = new LinkedHashMap<String, Object>(); 
    params.put("a", "1"); 
    params.put("b", "2"); 

    List<Object> cParamsLevel1 = new ArrayList<Object>(); 
    cParamsLevel1.add("cL1-val1"); 
    cParamsLevel1.add("cL12-val2"); 

    List<Object> cParamsLevel2 = new ArrayList<Object>(); 
    cParamsLevel2.add("cL2-val1"); 
    cParamsLevel2.add("cL2-val2"); 
    cParamsLevel1.add(cParamsLevel2); 

    params.put("c", cParamsLevel1); 

    System.out.println(httpBuildQuery(params, "UTF-8")); 
} 


private static void testCompound() { 

    Map<String, Object> params = new LinkedHashMap<String, Object>(); 

    //flat 
    params.put("a", "1"); 
    params.put("b", "2"); 

    //Map level 1 
    Map<String, Object> cParamsLevel1 = new LinkedHashMap<String, Object>(); 
    cParamsLevel1.put("cL1-1", "cLevel1-1val"); 
    cParamsLevel1.put("cL1-2", "cLevel1-2val"); 

    //Map level 2 
    Map<String, Object> cParamsLevel2 = new LinkedHashMap<String, Object>(); 
    cParamsLevel2.put("cL2-1", "cLevel2-1val"); 
    cParamsLevel2.put("cL2-2", "cLevel2-2val"); 
    cParamsLevel1.put("cL1-3", cParamsLevel2); 

    params.put("c", cParamsLevel1); 

    //List level 1 
    List<Object> dParamsLevel1 = new ArrayList<Object>(); 
    dParamsLevel1.add("dL1-val1"); 
    dParamsLevel1.add("dL12-val2"); 

    //List level 2 
    List<Object> dParamsLevel2 = new ArrayList<Object>(); 
    dParamsLevel2.add("dL2-val1"); 
    dParamsLevel2.add("dL2-val2"); 
    dParamsLevel1.add(dParamsLevel2); 

    params.put("d", dParamsLevel1); 

    System.out.println(httpBuildQuery(params, "UTF-8")); 

} 

} 

Para facilitar la prueba de sus resultados añadir la cadena resultado de las pruebas como cadena de consulta de la URL real que apunte a esta PHP. Ejemplo

 
http://localhost/test.php?a=1&b=2&c[cL1-1]=cLevel1-1val&c[cL1-2]=cLevel1-2val&c[cL1-3][cL2-1]=cLevel2-1val&c[cL1-3][cL2-2]=cLevel2-2val&d[0]=dL1-val1&d[1]=dL12-val2&d[2][0]=dL2-val1&d[2][1]=dL2-val2 
<?php 
var_dump($_REQUEST); 
?> 
+0

Creo que esta debería ser la respuesta correcta. –

1

Esta debe ser la solución más fácil (y más fiable): Uso

protected static String httpBuildQuery(List<? extends NameValuePair> parameters, String encoding) { 
    return URLEncodedUtils.format(parameters, encoding).replace("*", "%2A"); 
} 

Ejemplo:

List<NameValuePair> params = new ArrayList<NameValuePair>; 
params.add(new BasicNameValuePair("key", "value")); 

String queryString = httpBuildQuery(myParamList, "UTF-8"); 

Java no codifica el asterisco (+), mientras que PHP lo codifica %2A debería ser la única diferencia.

Cuestiones relacionadas