Analyser une chaîne URI dans une collection nom-valeur

274

J'ai l'URI comme ceci:

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

J'ai besoin d'une collection avec des éléments analysés:

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

Pour être exact, j'ai besoin d'un équivalent Java pour la méthode C # /. NET HttpUtility.ParseQueryString .

S'il vous plaît, donnez-moi un conseil à ce sujet.

Merci.

Sergey Shafiev
la source
1
duplication possible des chaînes de requête d'analyse en Java
Matt Ball
@MattBall Si l'OP utilise Android, alors c'est
Juan Mendes
156
N'est-il pas ahurissant que cela ne fasse pas partie de l'API de base de java.net.URI/ java.net.URL?
Dilum Ranatunga
Veuillez vérifier cette solution - bibliothèque solide et exemple de travail pour les opérations d'
analyse

Réponses:

342

Si vous cherchez un moyen d'y parvenir sans utiliser de bibliothèque externe, le code suivant vous aidera.

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

Vous pouvez accéder à la carte retournée en utilisant <map>.get("client_id"), avec l'URL indiquée dans votre question, cela retournerait "SS".

UPDATE URL-Decoding ajouté

MISE À JOUR Comme cette réponse est encore assez populaire, j'ai fait une version améliorée de la méthode ci-dessus, qui gère plusieurs paramètres avec la même clé et des paramètres sans valeur également.

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

METTRE À JOUR la version 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);
}

Exécution de la méthode ci-dessus avec l'URL

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

renvoie cette carte:

{param1=["value1"], param2=[null], param3=["value3", null]}
Pr0gr4mm3r
la source
13
Vous oubliez de décoder les noms et les paramètres, l'une des raisons pour lesquelles il est généralement préférable de laisser les bibliothèques effectuer des tâches courantes.
Juan Mendes
10
en effet, vous avez raison ... mais personnellement, je préfère écrire de telles tâches "faciles" par moi-même, au lieu d'utiliser une bibliothèque pour chaque tâche que j'ai à faire.
Pr0gr4mm3r
2
si vous avez plusieurs paramètres avec le même nom / clé, l'utilisation de cette fonction remplacera la valeur qui a une clé similaire.
snowball147
4
@Chris Vous confondez l'échappement xml / html avec l'encodage URL. Votre exemple d'URL doit être: a.com/q?1=a%26b&2=b%26c
sceaj
3
ce serait bien d'indiquer quelles fonctions sont utilisées: Collectors.mapping (...) et Collectors.toList (...)
Thomas Rebele
311

org.apache.http.client.utils.URLEncodedUtils

est une bibliothèque bien connue qui peut le faire pour vous

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

Les sorties

one : 1
two : 2
three : 3
three : 3a
Juan Mendes
la source
Puis-je recevoir la valeur par son nom sans passer tous les éléments? Je veux dire quelque chose comme ceci: System.out.print (params ["one"]);
Sergey Shafiev
3
@SergeyShafiev Il est trivial de convertir un List<NameValuePair>en Map<String,String>Java qui n'a pas accès aux crochets pour les cartes de hachage, cela ressemblerait à map.get("one")Si vous ne savez pas comment faire, ce devrait être une autre question (mais essayez d'abord par vous-même) . Nous préférons garder les questions minces ici à SO
Juan Mendes
6
Attention, si vous avez deux fois le même paramètre dans votre URL (c'est-à-dire? A = 1 & a = 2), URLEncodedUtils lèvera une exception IllegalArgumentException
Crystark
10
@Crystark Depuis httpclient 4.3.3, la chaîne de requête avec des noms dupliqués ne lève aucune exception. Cela fonctionne comme prévu. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));affichera [foo = bar, foo = baz] .
Akihiro HARAI
4
Depuis Android 6, la bibliothèque client HTTP Apache a été supprimée. Cela signifie que URLEncodedUtils and NameValuePair` n'est plus disponible (sauf si vous ajoutez une dépendance à la bibliothèque Apache héritée comme décrit ici ).
Ted Hopp
109

Si vous utilisez 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));
}

Tu auras:

param1: ab
param2: cd,ef
xxg
la source
1
pour l'utilisation des URLUriComponentsBuilder.fromHttpUrl(url)
Lu55
51

utilisez google Guava et faites-le en 2 lignes:

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

ce qui vous donne

{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
Amin Abbaspour
la source
18
Qu'en est-il du décodage d'URL décrit dans la réponse sélectionnée?
Clint Eastwood
7
Cela est également suspect pour plusieurs clés portant le même nom. Selon les javadocs, cela lèvera une exception IllegalArgumentException
jontro
5
Au lieu de fractionner manuellement, urivous devez utiliser new java.net.URL(uri).getQuery()car cela vous achète une validation d'entrée gratuite sur l'URL.
avgvstvs
1
Pour le décodage: final Map <String, String> queryVars = Maps.transformValues ​​(map, new Function <String, String> () {@Override public String apply (String value) {try {return URLDecoder.decode (value, "UTF- 8 ");} catch (UnsupportedEncodingException e) {// TODO Bloc de capture généré automatiquement e.printStackTrace ();} valeur de retour;}});
phreakhead
11
AVERTISSEMENT!! Ce n'est PAS sûr de le faire car splitter.split()jettera IllegalArgumentExceptions'il y a une clé en double dans la chaîne de requête. Voir stackoverflow.com/questions/1746507/…
Anderson
31

Le moyen le plus court que j'ai trouvé est celui-ci:

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

MISE À JOUR: UriComponentsBuilder vient du printemps. Voici le lien .

Dmitry Senkovich
la source
3
Sans savoir d'où vient cette classe UriComponentsBuilder, ce n'est pas très utile.
Thomas Mortagne
3
Il convient de noter que ce n'est une bonne idée que si vous utilisez déjà Spring. Si vous n'utilisez pas Spring, vous voudrez éviter. samatkinson.com/why-i-hate-spring
Nick
1
NB Cela prend les URI. La version Java des URI n'est pas un surensemble d'URL (c'est pourquoi toURI peut lever des exceptions).
Adam Gent
18

Pour Android, si vous utilisez OkHttp dans votre projet. Vous pourriez y jeter un œil. C'est simple et utile.

final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
    final String target = url.queryParameter("target");
    final String id = url.queryParameter("id");
}
THANN Phearum
la source
HttpUrl est une sorte de nom étrange mais c'est exactement ce dont j'avais besoin. Merci.
GuiSim
10

Java 8 one statement

Étant donné l'URL à analyser:

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

Cette solution collecte une liste de paires:

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

Cette solution recueille en revanche une carte (étant donné que dans une url il peut y avoir plus de paramètres avec le même nom mais des valeurs différentes).

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

Les deux solutions doivent utiliser une fonction utilitaire pour décoder correctement les paramètres.

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);
    }
}
Liberté
la source
4
Il s'agit plus d'une approche Java 8 que d'un oneliner Java 8.
Stephan
@Stephan bien :) peut-être les deux. Mais je suis plus intéressé à comprendre si vous aimez cette solution.
freedev
3
OMI, un oneliner doit être court et ne doit pas s'étendre sur plusieurs lignes.
Stephan
1
Il y a plusieurs déclarations impliquées ici.
Stephan
2
Je suppose que vous pourriez écrire toute une classe sur une seule ligne, mais ce n'est pas ce que l'on entend généralement par l'expression "à une ligne".
Abhijit Sarkar
10

Si vous utilisez le servlet, essayez ceci

request.getParameterMap()

Renvoie un java.util.Map des paramètres de cette demande.

Renvoie: un java.util.Map immuable contenant les noms des paramètres sous forme de clés et les valeurs des paramètres sous forme de valeurs de carte. Les clés de la mappe de paramètres sont de type String. Les valeurs de la mappe de paramètres sont de type tableau de chaînes.

( Doc Java )

Ansar Ozden
la source
Cela fonctionne avec Spring Web ainsi que dans votre contrôleur, vous pouvez avoir un paramètre de type HttpServletRequestet cela fonctionne MockHttpServletRequestaussi bien dans les tests unitaires Mock MVC.
GameSalutes
8

Si vous utilisez Java 8 et que vous souhaitez écrire quelques méthodes réutilisables, vous pouvez le faire sur une seule ligne.

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

Mais c'est une ligne assez brutale.

Fuwjax
la source
3

Sur Android, il existe une classe Uri dans le package android.net . Notez que Uri fait partie de android.net , tandis que URI fait partie de java.net .

La classe Uri a de nombreuses fonctions pour extraire des paires clé-valeur d'une requête. entrez la description de l'image ici

La fonction suivante renvoie des paires clé-valeur sous la forme de HashMap.

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

À 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
    }
Ramakrishna Joshi
la source
2

En utilisant les commentaires et solutions mentionnés ci-dessus, je stocke tous les paramètres de requête en utilisant Map <String, Object> où les objets peuvent être soit string soit Set <String>. La solution est donnée ci-dessous. Il est recommandé d'utiliser une sorte de validateur d'URL pour valider d'abord l'URL, puis d'appeler la méthode 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;
    }
}
SUMIT
la source
2

J'ai essayé une version Kotlin pour voir comment c'est le meilleur résultat sur 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
}

Et les méthodes d'extension

fun List<String>.getOrEmpty(index: Int) : String {
    return getOrElse(index) {""}
}

fun String.decodeToUTF8(): String { 
    URLDecoder.decode(this, "UTF-8")
}
Graham Smith
la source
1
Crédit égal à stackoverflow.com/users/1203812/matthew-herod 50/50 effort mais ne peut pas être co-auteur.
Graham Smith
2

Une solution prête à l'emploi pour le décodage de la partie de requête URI (y compris le décodage et les valeurs multi-paramètres)

commentaires

Je n'étais pas satisfait du code fourni par @ Pr0gr4mm3r dans https://stackoverflow.com/a/13592567/1211082 . La solution basée sur Stream ne fait pas URLDecoding, la version mutable clumpy.

J'ai donc élaboré une solution qui

  • Peut décomposer une partie de requête URI en un Map<String, List<Optional<String>>>
  • Peut gérer plusieurs valeurs pour le même nom de paramètre
  • Peut représenter les paramètres sans valeur correctement ( Optional.empty()au lieu de null)
  • Décode correctement les noms et les valeurs des paramètres viaURLdecode
  • Est basé sur Java 8 Streams
  • Est directement utilisable (voir le code incluant les importations ci-dessous)
  • Permet une gestion correcte des erreurs (ici en transformant une exception vérifiée UnsupportedEncodingExceptionen une exception d'exécution RuntimeUnsupportedEncodingExceptionqui permet l'interaction avec le flux. (L'encapsulation de fonctions régulières dans des fonctions lançant des exceptions vérifiées est un problème. Et Scala Tryn'est pas disponible dans le langage Java par défaut.)

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

Code Scala

... et par souci d'exhaustivité, je ne peux pas résister à fournir la solution en Scala qui domine par la brièveté et la beauté

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))
  }
}
Martin Senne
la source
1

Juste une mise à jour de la version 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())));
}

les méthodes de mappage et toList () doivent être utilisées avec les collecteurs, ce qui n'était pas mentionné dans la première réponse. Sinon, cela générerait une erreur de compilation dans l'IDE

Aarish Ramesh
la source
semble que vous devez également partager votre splitQueryParameters()méthode? Et qu'est-ce que c'est **Collectors**?
Kirby
1

Réponse de Kotlin avec référence initiale de https://stackoverflow.com/a/51024552/3286489 , mais avec une version améliorée en rangeant les codes et en fournissant 2 versions, et en utilisant des opérations de collecte immuables

Utilisez java.net.URIpour extraire la requête. Utilisez ensuite les fonctions d'extension fournies ci-dessous

  1. En supposant que vous ne souhaitiez que la dernière valeur de la requête, c'est page2&page3-à- dire qu'elle obtienne {page=3}, utilisez la fonction d'extension ci-dessous
    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. En supposant que vous voulez une liste de toutes les valeurs de la requête, c'est page2&page3-à- dire{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"

La façon de l'utiliser comme ci-dessous

    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}
Elye
la source
1

Netty fournit également un joli analyseur de chaîne de requête appelé QueryStringDecoder. Sur une ligne de code, il peut analyser l'URL dans la question. J'aime parce qu'il ne nécessite pas d'attraper ou de lancerjava.net.MalformedURLException .

En une seule ligne:

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

Voir javadocs ici: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Voici un court exemple, autonome et correct:

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

qui génère

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

Répondre ici parce que c'est un fil populaire. Il s'agit d'une solution propre dans Kotlin qui utilise l' UrlQuerySanitizerAPI recommandée . Voir la documentation officielle . J'ai ajouté un générateur de chaînes pour concaténer et afficher les paramètres.

    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")
    }
jungledev
la source
0

Voici ma solution avec réduire et facultatif :

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;
}
  • J'ignore les paramètres en double (je prends le dernier).
  • J'utilise Optional<SimpleImmutableEntry<String, String>>pour ignorer les ordures plus tard
  • La réduction commence avec une carte vide, puis remplissez-la sur chaque SimpleImmutableEntry

Dans le cas où vous demandez, la réduction nécessite ce combineur étrange dans le dernier paramètre, qui n'est utilisé que dans les flux parallèles. Son objectif est de fusionner deux résultats intermédiaires (ici HashMap).

Mike Dudley
la source
-1

Si vous utilisez Spring, ajoutez un argument de type @RequestParam Map<String,String> à votre méthode de contrôleur, et Spring construira la carte pour vous!

mancini0
la source