Analizzare una stringa URI nella raccolta di valori nominali

Ho l’URI in questo modo:

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

Ho bisogno di una collezione con elementi analizzati:

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

Per essere precisi, ho bisogno di un equivalente Java per il metodo C # HttpUtility.ParseQueryString. Per favore, dammi un consiglio su questo. Grazie.

Se stai cercando un modo per raggiungerlo senza utilizzare una libreria esterna, il seguente codice ti aiuterà.

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

Puoi accedere alla Mappa restituita usando

.get("client_id")

, con l’URL fornito nella tua domanda questo restituirà “SS”.

AGGIORNAMENTO Decodifica URL aggiunto

AGGIORNARE Poiché questa risposta è ancora abbastanza popolare, ho apportato una versione migliorata del metodo sopra, che gestisce più parametri con la stessa chiave e parametri senza valore.

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

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

Esecuzione del metodo precedente con l’URL

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

ritorna questa mappa:

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

org.apache.http.client.utils.URLEncodedUtils

è una libreria ben nota che può farlo per te

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

Uscite

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

Se stai usando 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)); } 

Otterrete:

 param1: abc param2: def,ghi 

usa google Guava e fallo in 2 righe:

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

che ti dà

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

Il modo più breve che ho trovato è questo:

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

AGGIORNAMENTO: UriComponentsBuilder viene dalla spring. Qui il link .

Se stai utilizzando Java 8 e sei disposto a scrivere alcuni metodi riutilizzabili, puoi farlo in un’unica riga.

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

Ma questa è una linea piuttosto brutale.

Java 8 una dichiarazione

Dato l’URL da analizzare:

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

Questa soluzione raccoglie un elenco di coppie:

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

Questa soluzione invece raccoglie una mappa (dato che in un url ci possono essere più parametri con lo stesso nome ma valori diversi).

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

Entrambe le soluzioni devono utilizzare una funzione di utilità per decodificare correttamente i parametri.

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

Per Android, se stai usando OkHttp nel tuo progetto. Potresti dare un’occhiata a questo. È semplice e utile.

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

Se usi la servlet doGet prova questo

 request.getParameterMap() 

Restituisce un java.util.Map dei parametri di questa richiesta.

Restituisce: un java.util.Map immutabile contenente i nomi dei parametri come chiavi e valori dei parametri come valori di mappa. Le chiavi nella mappa dei parametri sono di tipo String. I valori nella mappa dei parametri sono di tipo String array.

( Java doc )

Usando i commenti e le soluzioni sopra menzionati, sto memorizzando tutti i parametri della query usando Map dove Objects può essere stringa o Set . La soluzione è riportata di seguito. Si consiglia di utilizzare una sorta di validatore url per convalidare prima l’url e quindi chiamare il metodo 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; } } 

Se stai usando Spring, aggiungi un argomento di tipo @RequestParam Map al tuo metodo controller e Spring costruirà la mappa per te!

Solo un aggiornamento alla versione 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()))); } 

i metodi mapping e toList () devono essere utilizzati con Collector che non è stato menzionato nella risposta principale. Altrimenti verrebbe generato errore di compilazione in IDE

Ho provato una versione di Kotlin vedendo come questo è il risultato migliore in 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 } 

E i metodi di estensione

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