2009-03-11 18 views
7

Ho una mappa con i miei dati e voglio creare una stringa di query con essa, proprio come farei con http_build_query su PHP. Non sono sicuro se questo codice è la migliore implementazione o se sto dimenticando qualcosa?Esiste un equivalente Java della funzione http_build_query di 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

Il tuo codice funziona? Dov'è il problema? Qual'è la domanda? –

+0

Il mio codice funziona. La mia domanda era se c'è qualche classe o metodo che lo fa in un modo migliore. Non mi piace reinventare la ruota se c'è qualcosa di pronto che potrebbe avere un approccio migliore rispetto al mio codice. – falmp

risposta

6

un'occhiata alla classe QueryStringBuilder e la sua 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

Sto accettando questa risposta perché è esattamente quello che ho chiesto. Sto mantenendo la mia versione anche se ero abbastanza vicino ad essa. :) – falmp

+0

Sembra carino, dovresti prendere in considerazione la possibilità di confezionarlo o inviarlo come libreria. Un piccolo cavillo, il nome è fuorviante: non si tratta solo di costruire una stringa di query, ma include anche un percorso. – pimlottc

+0

Questo è un collegamento interrotto ora. – Flimm

0

sembra ok, con queste precisazioni:

  • rendono il parametro a Map<String, String> piuttosto che lanciare chiave e il valore di stringa.
  • la codifica hardcoded sembra sospetta. UTF-8 non è un dato, deve corrispondere alla codifica definita nell'intestazione della richiesta HTTP. Quindi il codice dovrebbe garantire che lo facciano - almeno definirlo come una costante da qualche parte e fare riferimento a quello qui e ovunque sia impostata la codifica della richiesta.

Modifica: Sembra che mi sbagliavo riguardo alla codifica; I parametri GET HTTP non sono soggetti a un'intestazione di codifica e tradizionalmente non hanno una codifica ben definita. RFC 3988 sembra imporre UTF-8, ma questo suona piuttosto fragile per me, quindi, a meno che tu non abbia stretto controllo sul server e possa garantire che usi effettivamente anche UTF-8, utilizzerei le richieste POST per qualsiasi dato che sia non nell'intervallo ASCII a 7 bit.

+0

a) Risolto il problema, grazie. b) Sembra che tu abbia ragione, ma sto usando il restlet.pacchetti org per fare una richiesta REST e non consente di definire la codifica della richiesta. Diciamo che non ne specifica uno, quale sarebbe l'impostazione predefinita? Dipende dalla configurazione del server? – falmp

1

Si sarebbe probabilmente desidera controllare l'intestazione della richiesta "Accetta" per le codifiche supportati dal client prima di forzare UTF-8 (anche se è probabilmente la scelta migliore).

2

Il vero potere della funzione http_build_query di PHP è la sua capacità di ottenere array associativo e tradurlo in stringa di URL. Il codice seguente fa una cosa simile e consente di costruire parametri url come mappe a più livelli che includono mappe e raccolte nidificate. Con un po 'di lavoro in più, è possibile aggiungere anche un supporto per Array.

I metodi di prova sono mostrati di seguito.

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

} 

} 

Per una facile prova dei risultati aggiungere la stringa provocato da test come stringa di query di URL reale che puntano a questo PHP. Esempio

 
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

Penso che questa dovrebbe essere la risposta corretta. –

1

Questa dovrebbe essere la soluzione più semplice (e più affidabile): utilizzo

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

Esempio:

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

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

Java non codifica l'asterisco (+) mentre PHP lo codifica %2A dovrebbe essere l'unica differenza.