Analizzare una stringa URI nella raccolta nome-valore


274

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 l'esattezza, ho bisogno di un equivalente Java per il metodo C # /. NET HttpUtility.ParseQueryString .

Per favore, dammi un consiglio su questo.

Grazie.


1
possibile duplicato delle stringhe di query
Matt Ball,

@MattBall Se l'OP utilizza Android, lo è
Juan Mendes,

156
Non è sbalorditivo che questo non faccia parte dell'API principale di java.net.URI/ java.net.URL?
Dilum Ranatunga,

Si prega di verificare questa soluzione - libreria solida e esempio di lavoro sia per le operazioni di analizzare e formattare: stackoverflow.com/a/37744000/1882064
arcseldon

Risposte:


342

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

public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
    Map<String, String> query_pairs = new LinkedHashMap<String, String>();
    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 <map>.get("client_id"), con l'URL indicato nella tua domanda questo restituirebbe "SS".

AGGIORNATO decodifica URL aggiunto

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

public static Map<String, List<String>> splitQuery(URL url) throws UnsupportedEncodingException {
  final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
  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<String>());
    }
    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;
}

AGGIORNAMENTO versione Java8

public Map<String, List<String>> 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<String, String> 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 sopra con l'URL

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

restituisce questa mappa:

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

13
Stai dimenticando di decodificare i nomi e i parametri, uno dei motivi per cui di solito è meglio lasciare che le librerie facciano compiti comuni.
Juan Mendes,

10
anzi, hai ragione ... ma personalmente preferisco scrivere tali compiti "facili" da solo, invece di usare una propria libreria per ogni singolo compito che devo fare.
Pr0gr4mm3r,

2
se si dispone di più parametri con lo stesso nome / chiave, l'utilizzo di questa funzione sostituirà il valore con chiave simile.
snowball147

4
@Chris Stai confondendo l'escape xml / html con la codifica URL. L'URL di esempio dovrebbe essere: a.com/q?1=a%26b&2=b%26c
sceaj

3
sarebbe bello indicare quali funzioni sono usate: Collectors.mapping (...) e Collectors.toList (...)
Thomas Rebele

311

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<NameValuePair> 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

Posso ricevere il valore per nome senza passare tutti gli elementi? Intendo qualcosa del genere: System.out.print (params ["one"]);
Sergey Shafiev,

3
@SergeyShafiev È banale convertire un file a List<NameValuePair>in Map<String,String>Java che non ha un accesso a parentesi per le mappe hash, sembrerebbe che map.get("one")se non sai come farlo, dovrebbe essere un'altra domanda (ma prima prova da solo) . Preferiamo evitare le domande qui a SO
Juan Mendes,

6
Fai attenzione che se hai due volte lo stesso parametro nel tuo URL (cioè? A = 1 & a = 2) URLEncodedUtils genererà un IllegalArgumentException
Crystark,

10
@Crystark A partire da httpclient 4.3.3, la stringa di query con nomi duplicati non genera alcuna eccezione. Funziona come previsto. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));stamperà [foo = bar, foo = baz] .
Akihiro HARAI il

4
A partire da Android 6, la libreria client HTTP Apache è stata rimossa. Ciò significa che URLEncodedUtils and NameValuePair` non è più disponibile (a meno che non si aggiunga una dipendenza alla libreria Apache legacy come descritto qui ).
Ted Hopp,

109

Se si utilizza Spring Framework:

public static void main(String[] args) {
    String uri = "http://my.test.com/test?param1=ab&param2=cd&param2=ef";
    MultiValueMap<String, String> parameters =
            UriComponentsBuilder.fromUriString(uri).build().getQueryParams();
    List<String> param1 = parameters.get("param1");
    List<String> param2 = parameters.get("param2");
    System.out.println("param1: " + param1.get(0));
    System.out.println("param2: " + param2.get(0) + "," + param2.get(1));
}

Otterrete:

param1: ab
param2: cd,ef

1
per l'utilizzo degli URLUriComponentsBuilder.fromHttpUrl(url)
Lu55,

51

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<String, String> 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}

18
Che dire della decodifica URL descritta nella risposta selezionata?
Clint Eastwood,

7
Questo è anche sospetto di più chiavi con lo stesso nome. Secondo il javadocs questo genererà un'eccezione IllegalArgumentException
jontro

5
Invece di dividere manualmente uri, dovresti usare new java.net.URL(uri).getQuery()questo perché ti compra la convalida dell'input gratuito sull'URL.
avgvstvs,

1
Per la decodifica: final Map <String, String> queryVars = Maps.transformValues ​​(mappa, nuova funzione <String, String> () {@Override public String apply (valore String) {try {return URLDecoder.decode (value, "UTF- 8 ");} catch (UnsupportedEncodingException e) {// TODO Blocco catch generato automaticamente e.printStackTrace ();} valore restituito;}});
phreakhead,

11
AVVERTIMENTO!! NON è sicuro farlo poiché splitter.split()genererà una IllegalArgumentExceptionchiave duplicata nella stringa di query. Vedi stackoverflow.com/questions/1746507/…
Anderson,

31

Il modo più breve che ho trovato è questo:

MultiValueMap<String, String> queryParams =
            UriComponentsBuilder.fromUriString(url).build().getQueryParams();

AGGIORNAMENTO: UriComponentsBuilder viene dalla primavera. Qui il link .


3
Senza sapere da dove proviene questa classe UriComponentsBuilder non è molto utile.
Thomas Mortagne,

3
Vale la pena notare che questa è una buona idea se stai già utilizzando Spring. Se non stai usando Spring, vorrai evitare. samatkinson.com/why-i-hate-spring
Nick

1
NB Questo richiede URI. La versione di URI di Java non è un superset di URL (ecco perché toURI può generare eccezioni).
Adam Gent,

18

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

HttpUrl è una specie di nome strano ma questo è esattamente ciò di cui avevo bisogno. Grazie.
GuiSim,

10

Java 8 un'istruzione

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<AbstractMap.SimpleEntry<String, String>> list = 
        Pattern.compile("&").splitAsStream(url.getQuery())
        .map(s -> Arrays.copyOf(s.split("="), 2))
        .map(o -> new AbstractMap.SimpleEntry<String, String>(decode(o[0]), decode(o[1])))
        .collect(toList());

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

Map<String, List<String>> 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);
    }
}

4
Questo è più un approccio Java 8 piuttosto che un oneliner Java 8.
Stephan,

@Stephan bene :) forse entrambi. Ma sono più interessato a capire se ti piace questa soluzione.
Freedev,

3
IMO, un oneliner dovrebbe essere corto e non dovrebbe estendersi su più linee.
Stephan,

1
Ci sono più dichiarazioni coinvolte qui.
Stephan,

2
Suppongo che potresti scrivere un'intera classe su una sola riga, ma non è quello che di solito si intende con la frase "one-liner".
Abhijit Sarkar,

10

Se stai usando servlet doGet, prova questo

request.getParameterMap()

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

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

( Documento Java )


Questo funziona con Spring Web e nel controller è possibile avere un parametro di tipo HttpServletRequeste funziona MockHttpServletRequestanche nei test di unità Mock MVC.
GameSalutes

8

Se stai usando Java 8 e sei disposto a scrivere alcuni metodi riutilizzabili, puoi farlo in una riga.

private Map<String, List<String>> 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 <T> List<T> mergeLists(final List<T> l1, final List<T> l2) {
    List<T> list = new ArrayList<>();
    list.addAll(l1);
    list.addAll(l2);
    return list;
}

private static <T> 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.


3

Su Android, esiste una classe Uri nel pacchetto android.net . Nota che Uri fa parte di android.net , mentre URI fa parte di java.net .

La classe Uri ha molte funzioni per estrarre coppie chiave-valore da una query. inserisci qui la descrizione dell'immagine

La seguente funzione restituisce coppie chiave-valore sotto forma di HashMap.

In Java:

Map<String, String> getQueryKeyValueMap(Uri uri){
    HashMap<String, String> keyValueMap = new HashMap();
    String key;
    String value;

    Set<String> keyNamesList = uri.getQueryParameterNames();
    Iterator iterator = keyNamesList.iterator();

    while (iterator.hasNext()){
        key = (String) iterator.next();
        value = uri.getQueryParameter(key);
        keyValueMap.put(key, value);
    }
    return keyValueMap;
}

A Kotlin:

fun getQueryKeyValueMap(uri: Uri): HashMap<String, String> {
        val keyValueMap = HashMap<String, String>()
        var key: String
        var value: String

        val keyNamesList = uri.queryParameterNames
        val iterator = keyNamesList.iterator()

        while (iterator.hasNext()) {
            key = iterator.next() as String
            value = uri.getQueryParameter(key) as String
            keyValueMap.put(key, value)
        }
        return keyValueMap
    }

2

Utilizzando i commenti e le soluzioni sopra menzionati, sto memorizzando tutti i parametri della query usando Map <String, Object> dove gli oggetti possono essere stringa o Set <String>. La soluzione è riportata di seguito. Si consiglia di utilizzare un qualche tipo di validatore dell'URL per convalidare prima l'URL e quindi chiamare il metodo convertQueryStringToMap.

private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";

public static Map<String, Object> convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException {
    List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME);
    Map<String, Object> 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<String>(Arrays.asList(value.split(","))) : value;
    } else {
        Set<String> queryValueSet = responseMap.get(key) instanceof Set ? (Set<String>) responseMap.get(key) : new HashSet<String>();
        if (value.contains(",")) {
            queryValueSet.addAll(Arrays.asList(value.split(",")));
        } else {
            queryValueSet.add(value);
        }
        return queryValueSet;
    }
}

2

Ho provato una versione di Kotlin vedendo come questo è il risultato migliore in Google.

@Throws(UnsupportedEncodingException::class)
fun splitQuery(url: URL): Map<String, List<String>> {

    val queryPairs = LinkedHashMap<String, ArrayList<String>>()

    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<String>.getOrEmpty(index: Int) : String {
    return getOrElse(index) {""}
}

fun String.decodeToUTF8(): String { 
    URLDecoder.decode(this, "UTF-8")
}

1
Pari merito a stackoverflow.com/users/1203812/matthew-herod sforzo 50/50 ma non può essere coautore.
Graham Smith,

2

Una soluzione pronta per l'uso per la decodifica della parte della query URI (incl. Decodifica e valori multiparametro)

Commenti

Non ero contento del codice fornito da @ Pr0gr4mm3r in https://stackoverflow.com/a/13592567/1211082 . La soluzione basata su Stream non esegue URLDecoding, la versione mutabile maldestra.

Così ho elaborato una soluzione che

  • È possibile decomporre una parte della query URI in a Map<String, List<Optional<String>>>
  • Può gestire più valori con lo stesso nome di parametro
  • Può rappresentare correttamente i parametri senza un valore ( Optional.empty()anziché null)
  • Decodifica correttamente i nomi e i valori dei parametri tramiteURLdecode
  • Si basa su Java 8 Streams
  • È direttamente utilizzabile (vedere il codice incluse le importazioni di seguito)
  • Consente una corretta gestione degli errori (qui trasformando un'eccezione controllata UnsupportedEncodingExceptionin un'eccezione di runtime RuntimeUnsupportedEncodingExceptionche consente l'interazione con lo stream. (Avvolgere la funzione regolare in funzioni generando eccezioni verificate è una seccatura. E Scala Trynon è disponibile nel linguaggio predefinito Java).

Codice Java

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import static java.util.stream.Collectors.*;

public class URIParameterDecode {
    /**
     * Decode parameters in query part of a URI into a map from parameter name to its parameter values.
     * For parameters that occur multiple times each value is collected.
     * Proper decoding of the parameters is performed.
     * 
     * Example
     *   <pre>a=1&b=2&c=&a=4</pre>
     * is converted into
     *   <pre>{a=[Optional[1], Optional[4]], b=[Optional[2]], c=[Optional.empty]}</pre>
     * @param query the query part of an URI 
     * @return map of parameters names into a list of their values.
     *         
     */
    public static Map<String, List<Optional<String>>> splitQuery(String query) {
        if (query == null || query.isEmpty()) {
            return Collections.emptyMap();
        }

        return Arrays.stream(query.split("&"))
                    .map(p -> splitQueryParameter(p))
                    .collect(groupingBy(e -> e.get0(), // group by parameter name
                            mapping(e -> e.get1(), toList())));// keep parameter values and assemble into list
    }

    public static Pair<String, Optional<String>> splitQueryParameter(String parameter) {
        final String enc = "UTF-8";
        List<String> keyValue = Arrays.stream(parameter.split("="))
                .map(e -> {
                    try {
                        return URLDecoder.decode(e, enc);
                    } catch (UnsupportedEncodingException ex) {
                        throw new RuntimeUnsupportedEncodingException(ex);
                    }
                }).collect(toList());

        if (keyValue.size() == 2) {
            return new Pair(keyValue.get(0), Optional.of(keyValue.get(1)));
        } else {
            return new Pair(keyValue.get(0), Optional.empty());
        }
    }

    /** Runtime exception (instead of checked exception) to denote unsupported enconding */
    public static class RuntimeUnsupportedEncodingException extends RuntimeException {
        public RuntimeUnsupportedEncodingException(Throwable cause) {
            super(cause);
        }
    }

    /**
     * A simple pair of two elements
     * @param <U> first element
     * @param <V> second element
     */
    public static class Pair<U, V> {
        U a;
        V b;

        public Pair(U u, V v) {
            this.a = u;
            this.b = v;
        }

        public U get0() {
            return a;
        }

        public V get1() {
            return b;
        }
    }
}

Codice Scala

... e per completezza non posso resistere a fornire la soluzione alla Scala che domina per brevità e bellezza

import java.net.URLDecoder

object Decode {
  def main(args: Array[String]): Unit = {
    val input = "a=1&b=2&c=&a=4";
    println(separate(input))
  }

  def separate(input: String) : Map[String, List[Option[String]]] = {
    case class Parameter(key: String, value: Option[String])

    def separateParameter(parameter: String) : Parameter =
      parameter.split("=")
               .map(e => URLDecoder.decode(e, "UTF-8")) match {
      case Array(key, value) =>  Parameter(key, Some(value))
      case Array(key) => Parameter(key, None)
    }

    input.split("&").toList
      .map(p => separateParameter(p))
      .groupBy(p => p.key)
      .mapValues(vs => vs.map(p => p.value))
  }
}

1

Solo un aggiornamento alla versione Java 8

public Map<String, List<String>> 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 di mappatura e toList () devono essere usati con i Collezionisti che non sono stati menzionati nella risposta principale. Altrimenti genererebbe un errore di compilazione in IDE


sembra che tu debba condividere anche il tuo splitQueryParameters()metodo? E cosa succede con il **Collectors**?
Kirby,

1

La risposta di Kotlin con riferimento iniziale da https://stackoverflow.com/a/51024552/3286489 , ma con una versione migliorata riordinando i codici e fornendo 2 versioni di esso, e utilizzare operazioni di raccolta immutabili

Utilizzare java.net.URIper estrarre la query. Quindi utilizzare le funzioni di estensione fornite di seguito

  1. Supponendo che si desidera solo l'ultimo valore di interrogazione cioè page2&page3otterrà {page=3}, utilizza la funzione di estensione in basso
    fun URI.getQueryMap(): Map<String, String> {
        if (query == null) return emptyMap()

        return query.split("&")
                .mapNotNull { element -> element.split("=")
                        .takeIf { it.size == 2 && it.none { it.isBlank() } } }
                .associateBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
    }

    private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
  1. Supponendo che si desidera un elenco di tutti i valori per la query, ovvero page2&page3verrà visualizzato{page=[2, 3]}
    fun URI.getQueryMapList(): Map<String, List<String>> {
        if (query == null) return emptyMap()

        return query.split("&")
                .distinct()
                .mapNotNull { element -> element.split("=")
                        .takeIf { it.size == 2 && it.none { it.isBlank() } } }
                .groupBy({ it[0].decodeUTF8() }, { it[1].decodeUTF8() })
    }

    private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"

Il modo di usarlo come di seguito

    val uri = URI("schema://host/path/?page=&page=2&page=2&page=3")
    println(uri.getQueryMapList()) // Result is {page=[2, 3]}
    println(uri.getQueryMap()) // Result is {page=3}

1

Netty fornisce anche un buon parser di stringhe di query chiamato QueryStringDecoder. In una riga di codice, può analizzare l'URL nella domanda. Mi piace perché non richiede cattura o lancio java.net.MalformedURLException.

In una riga:

Map<String, List<String>> parameters = new QueryStringDecoder(url).parameters();

Vedi javadocs qui: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Ecco un esempio breve, autonomo e corretto:

import io.netty.handler.codec.http.QueryStringDecoder;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;

public class UrlParse {

  public static void main(String... args) {
    String url = "https://google.com.ua/oauth/authorize?client_id=SS&response_type=code&scope=N_FULL&access_type=offline&redirect_uri=http://localhost/Callback";
    QueryStringDecoder decoder = new QueryStringDecoder(url);
    Map<String, List<String>> parameters = decoder.parameters();
    print(parameters);
  }

  private static void print(final Map<String, List<String>> parameters) {
    System.out.println("NAME               VALUE");
    System.out.println("------------------------");
    parameters.forEach((key, values) ->
        values.forEach(val ->
            System.out.println(StringUtils.rightPad(key, 19) + val)));
  }
}

che genera

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

0

Rispondi qui perché questo è un thread popolare. Questa è una soluzione pulita in Kotlin che utilizza l' UrlQuerySanitizerAPI raccomandato . Vedi la documentazione ufficiale . Ho aggiunto un generatore di stringhe per concatenare e visualizzare i parametri.

    var myURL: String? = null

    if (intent.hasExtra("my_value")) {
        myURL = intent.extras.getString("my_value")
    } else {
        myURL = intent.dataString
    }

    val sanitizer = UrlQuerySanitizer(myURL)
    // We don't want to manually define every expected query *key*, so we set this to true
    sanitizer.allowUnregisteredParamaters = true
    val parameterNamesToValues: List<UrlQuerySanitizer.ParameterValuePair> = sanitizer.parameterList
    val parameterIterator: Iterator<UrlQuerySanitizer.ParameterValuePair> = parameterNamesToValues.iterator()

    // Helper simply so we can display all values on screen
    val stringBuilder = StringBuilder()

    while (parameterIterator.hasNext()) {
        val parameterValuePair: UrlQuerySanitizer.ParameterValuePair = parameterIterator.next()
        val parameterName: String = parameterValuePair.mParameter
        val parameterValue: String = parameterValuePair.mValue

        // Append string to display all key value pairs
        stringBuilder.append("Key: $parameterName\nValue: $parameterValue\n\n")
    }

    // Set a textView's text to display the string
    val paramListString = stringBuilder.toString()
    val textView: TextView = findViewById(R.id.activity_title) as TextView
    textView.text = "Paramlist is \n\n$paramListString"

    // to check if the url has specific keys
    if (sanitizer.hasParameter("type")) {
        val type = sanitizer.getValue("type")
        println("sanitizer has type param $type")
    }

0

Ecco la mia soluzione con ridurre e opzionale :

private Optional<SimpleImmutableEntry<String, String>> splitKeyValue(String text) {
    String[] v = text.split("=");
    if (v.length == 1 || v.length == 2) {
        String key = URLDecoder.decode(v[0], StandardCharsets.UTF_8);
        String value = v.length == 2 ? URLDecoder.decode(v[1], StandardCharsets.UTF_8) : null;
        return Optional.of(new SimpleImmutableEntry<String, String>(key, value));
    } else
        return Optional.empty();
}

private HashMap<String, String> parseQuery(URI uri) {
    HashMap<String, String> params = Arrays.stream(uri.getQuery()
            .split("&"))
            .map(this::splitKeyValue)
            .filter(Optional::isPresent)
            .map(Optional::get)
            .reduce(
                // initial value
                new HashMap<String, String>(), 
                // accumulator
                (map, kv) -> {
                     map.put(kv.getKey(), kv.getValue()); 
                     return map;
                }, 
                // combiner
                (a, b) -> {
                     a.putAll(b); 
                     return a;
                });
    return params;
}
  • Ignoro i parametri duplicati (prendo l'ultimo).
  • Uso Optional<SimpleImmutableEntry<String, String>>per ignorare la spazzatura più tardi
  • La riduzione inizia con una mappa vuota, quindi la popola su ogni SimpleImmutableEntry

Nel caso lo si chieda, ridurre richiede questo strano combinatore nell'ultimo parametro, che viene utilizzato solo in flussi paralleli. Il suo obiettivo è quello di unire due risultati intermedi (qui HashMap).


-1

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.