web-dev-qa-db-fra.com

Analyser une chaîne d'URI dans la collection Nom-Valeur

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, il me faut un équivalent Java pour la méthode C # HttpUtility.ParseQueryString . S'il vous plaît, donnez-moi un conseil à ce sujet . Merci.

208
Sergey Shafiev

Si vous cherchez un moyen de le réaliser 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 renvoyée à l'aide de <map>.get("client_id"). L'URL indiquée dans votre question renvoie "SS".

UPDATEURL-Decoding ajouté

UPDATEComme cette réponse est encore très populaire, j'ai mis au point 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.

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

MISE &AGRAVE; JOURversion 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écuter 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]}
281
Pr0gr4mm3r

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

Remarque: fonctionne uniquement avec HTTP PUT ou HTTP POST

259
Juan Mendes

Si vous utilisez Spring Framework:

public static void main(String[] args) {
    String uri = "http://youhost.com/test?param1=abc&param2=def&param2=ghi";
    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: abc
param2: def,ghi
64
xxg

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

qui te donne

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

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

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

UPDATE:UriComponentsBuilder vient du printemps. Ici le lien .

18
Dmitry Senkovich

Pour Android, si vous utilisez OkHttp dans votre projet. Vous pourriez avoir un coup d'oeil à ceci. 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");
}
8
THANN Phearum

Si vous utilisez Java 8 et que vous souhaitez écrire quelques méthodes réutilisables, vous pouvez le faire en 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.

8
Fuwjax

Java 8 une déclaration

É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, par contre, collecte une carte (étant donné que dans une URL, il peut y avoir plus de paramètres portant 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);
    }
}
5
freedev

Si vous utilisez servlet doGet essayez ceci

request.getParameterMap()

Retourne un Java.util.Map des paramètres de cette requête.

Résultats: une Java.util.Map immuable contenant des noms de paramètre sous forme de clés et des valeurs de paramètre sous forme de valeurs de carte Les clés de la mappe de paramètres sont de type String. Les valeurs dans la mappe de paramètres sont de type String array.

( Java doc )

5
Ansar Ozden

En utilisant les commentaires et les solutions susmentionnées, je stocke tous les paramètres de requête en utilisant Map <String, Object>, où les objets peuvent être string ou 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;
    }
}
2
SUMIT

Je me suis essayé à une version Kotlin en voyant que c'était le meilleur résultat obtenu par 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")
}
2
Graham Smith

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 mapping et toList () doivent être utilisées avec des collecteurs, ce qui n'était pas mentionné dans la réponse principale. Sinon, cela provoquerait une erreur de compilation dans l'IDE

2
Aarish Ramesh

Netty fournit également un analyseur syntaxique de chaînes de requête appelé QueryStringDecoder. Dans une ligne de code, il peut analyser l'URL de la question. J'aime parce que cela ne nécessite pas d'attraper ou de lancer Java.net.MalformedURLException.

En une 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 exemple court, 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
0
Kirby

Répondre ici parce que c'est un fil populaire. Ceci est une solution propre à Kotlin qui utilise l’application recommandée UrlQuerySanitizerVoir la documentation officielle . J'ai ajouté un constructeur 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")
    }
0
jungledev

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 par une carte vide, puis remplit-la sur chaque SimpleImmutableEntry.

Si vous le demandez, réduire requiert cet étrange combineur 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).

0
Mike Dudley

La réponse de Kotlin avec la 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 utilisant des opérations de collecte immuables

Utilisez Java.net.URI pour extraire la requête. Ensuite, utilisez les fonctions d'extension fournies ci-dessous

  1. En supposant que vous ne vouliez que la dernière valeur de la requête, c'est-à-dire que page2&page3 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 souhaitiez obtenir une liste de toutes les valeurs de la requête, i.e. page2&page3 obtiendra {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}
0
Elye

Sur Android, la classe Uri est présente dans le packageAndroid.net. Notez que Uri fait partie d'Android.net, tandis que URI fait partie de Java.net.

La classe Uri a de nombreuses fonctions pour extraire les paires clé-valeur de requête.  enter image description here 

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