Analisar uma seqüência de caracteres URI na coleção Nome-Valor

274

Eu tenho o URI assim:

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

Preciso de uma coleção com elementos analisados:

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

Para ser exato, preciso de um equivalente em Java para o método C # / .NET HttpUtility.ParseQueryString .

Por favor, me dê um conselho sobre isso.

Obrigado.

Sergey Shafiev
fonte
1
possível duplicata de seqüências de consulta Analisando em Java
Matt Bola
@MattBall Se o OP está usando Android, então é
Juan Mendes
156
Não é de surpreender que isso não faça parte da API principal de java.net.URI/ java.net.URL?
Dilum Ranatunga
Verifique esta solução - biblioteca sólida e exemplo de trabalho para as operações de
Análise

Respostas:

342

Se você estiver procurando uma maneira de alcançá-lo sem usar uma biblioteca externa, o código a seguir o ajudará.

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

Você pode acessar o mapa retornado usando <map>.get("client_id"), com o URL fornecido na sua pergunta, isso retornaria "SS".

UPDATE Decodificação de URL adicionada

ATUALIZAÇÃO Como essa resposta ainda é bastante popular, fiz uma versão aprimorada do método acima, que lida com vários parâmetros com a mesma chave e com parâmetros sem nenhum valor.

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

ATUALIZAÇÃO versão 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);
}

Executando o método acima com o URL

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

retorna este mapa:

{param1=["value1"], param2=[null], param3=["value3", null]}
Pr0gr4mm3r
fonte
13
Você está esquecendo de decodificar os nomes e parâmetros, uma razão pela qual geralmente é melhor permitir que as bibliotecas realizem tarefas comuns.
Juan Mendes
10
de fato, você está certo ... mas eu pessoalmente prefiro escrever tarefas "fáceis" sozinho, em vez de usar uma biblioteca própria para cada tarefa que tenho que fazer.
Pr0gr4mm3r
2
se você tiver vários parâmetros com o mesmo nome / chave, o uso dessa função substituirá o valor que possui chave semelhante.
precisa
4
@ Chris Você está confundindo xml / html com a codificação de URL. Seu URL de exemplo deve ser: a.com/q?1=a%26b&2=b%26c
sceaj
3
seria bom para indicar as funções utilizadas: Collectors.mapping (...) e Collectors.toList (...)
Thomas Rebele
311

org.apache.http.client.utils.URLEncodedUtils

é uma biblioteca bem conhecida que pode fazer isso por você

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

Saídas

one : 1
two : 2
three : 3
three : 3a
Juan Mendes
fonte
Posso receber o valor por seu nome sem passar todos os elementos? Quero dizer algo assim: System.out.print (params ["one"]);
Sergey Shafiev 27/11/2012
3
@SergeyShafiev É trivial converter um List<NameValuePair>em um Map<String,String>Java que não tenha acesso de colchete para mapas de hash, seria como map.get("one")se você não soubesse fazer isso, deveria ser outra pergunta (mas tente primeiro por conta própria) . Preferimos manter as perguntas reduzidas aqui no SO
Juan Mendes
6
Tenha cuidado que se você tem duas vezes o mesmo parâmetro em sua URL (ou seja, a = 1 & a = 2?) URLEncodedUtils irá lançar uma IllegalArgumentException
Crystark
10
@ Crystark A partir do httpclient 4.3.3, a string de consulta com nomes duplicados não gera nenhuma exceção. Funciona como esperado. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));irá imprimir [foo = bar, foo = baz] .
Akihiro HARAI
4
A partir do Android 6, a biblioteca do cliente HTTP Apache foi removida. Isso significa que URLEncodedUtils and NameValuePair` não está mais disponível (a menos que você adicione uma dependência à biblioteca Apache herdada, conforme descrito aqui ).
Ted Hopp
109

Se você estiver usando o 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));
}

Você vai ter:

param1: ab
param2: cd,ef
xxg
fonte
1
para uso de URLsUriComponentsBuilder.fromHttpUrl(url)
Lu55 11/02
51

use o Google Guava e faça-o em 2 linhas:

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

o que te dá

{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
Amin Abbaspour
fonte
18
E a decodificação de URL descrita na resposta selecionada?
Clint Eastwood
7
Também é suspeito de várias chaves com o mesmo nome. De acordo com os javadocs este irá lançar uma IllegalArgumentException
jontro
5
Em vez de dividir manualmente, urivocê deve usar, new java.net.URL(uri).getQuery()pois isso permite a validação gratuita da entrada no URL.
Avgvstvs 6/03/2015
1
Para decodificação: 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 Bloco de captura gerado automaticamente e.printStackTrace ();} valor de retorno;}});
Phreakhead
11
AVISO!! NÃO é seguro fazer isso, uma vez que splitter.split()será lançada IllegalArgumentExceptionse houver uma chave duplicada na string de consulta. Veja stackoverflow.com/questions/1746507/…
Anderson
31

A maneira mais curta que encontrei é esta:

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

UPDATE: UriComponentsBuilder vem da primavera. Aqui o link .

Dmitry Senkovich
fonte
3
Sem saber de onde vem essa classe UriComponentsBuilder, não é muito útil.
Thomas Mortagne
3
Pode ser interessante notar que isso é apenas uma boa ideia se você já estiver usando o Spring. Se você não estiver usando o Spring, convém evitar. samatkinson.com/why-i-hate-spring #
Nick
1
NB Isso leva URIs. A versão de URIs do Java não é um superconjunto de URLs (é por isso que o toURI pode lançar exceções).
Adam Gent
18

Para Android, se você estiver usando o OkHttp em seu projeto. Você pode dar uma olhada nisso. É simples e útil.

final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
    final String target = url.queryParameter("target");
    final String id = url.queryParameter("id");
}
THANN Phearum
fonte
HttpUrl é meio que um nome estranho, mas é exatamente isso que eu precisava. Obrigado.
precisa saber é o seguinte
10

Java 8 uma declaração

Dado o URL para analisar:

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

Esta solução coleta uma lista de pares:

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

Por outro lado, esta solução coleta um mapa (dado que em um URL pode haver mais parâmetros com o mesmo nome, mas com valores diferentes).

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

Ambas as soluções devem usar uma função de utilitário para decodificar adequadamente os parâmetros.

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);
    }
}
freedev
fonte
4
Essa é mais uma abordagem do Java 8 do que um oneliner do Java 8.
Stephan
@ Stephen bem :) talvez ambos. Mas estou mais interessado em entender se você gosta desta solução.
freedev
3
Na IMO, um oneliner deve ser curto e não deve se estender por várias linhas.
Stephan
1
Existem várias declarações envolvidas aqui.
Stephan
2
Eu acho que você poderia escrever uma classe inteira em uma única linha, mas não é o que geralmente se entende pela frase "one-liner".
Abhijit Sarkar
10

Se você estiver usando o servlet doGet, tente isso

request.getParameterMap()

Retorna um java.util.Map dos parâmetros desta solicitação.

Retorna: um java.util.Map imutável que contém nomes de parâmetros como chaves e valores de parâmetros como valores de mapa. As chaves no mapa de parâmetros são do tipo String. Os valores no mapa de parâmetros são do tipo String array.

( Doc Java )

Ansar Ozden
fonte
Isso funciona com o Spring Web e no seu controlador, você pode ter um parâmetro do tipo HttpServletRequeste MockHttpServletRequesttambém nos testes de unidade do Mock MVC.
GameSalutes 23/05/19
8

Se você estiver usando o Java 8 e quiser escrever alguns métodos reutilizáveis, poderá fazê-lo em uma linha.

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

Mas essa é uma linha bastante brutal.

Fuwjax
fonte
3

No Android, há uma classe Uri no pacote android.net . Observe que Uri faz parte do android.net , enquanto o URI faz parte do java.net .

A classe Uri possui muitas funções para extrair pares de valores-chave de uma consulta. insira a descrição da imagem aqui

A função a seguir retorna pares de valores-chave na forma de HashMap.

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

Em 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
fonte
2

Usando os comentários e soluções mencionados acima, estou armazenando todos os parâmetros de consulta usando Map <String, Object>, em que os objetos podem ser string ou Set <String>. A solução é dada abaixo. É recomendável usar algum tipo de validador de URL para validar o URL primeiro e depois chamar o método 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
fonte
2

Eu experimentei uma versão do Kotlin, vendo como esse é o melhor resultado no 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 os métodos de extensão

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

fun String.decodeToUTF8(): String { 
    URLDecoder.decode(this, "UTF-8")
}
Graham Smith
fonte
1
Crédito igual ao esforço stackoverflow.com/users/1203812/matthew-herod 50/50, mas não pode ser co-autor.
Graham Smith
2

Uma solução pronta para uso para decodificação da parte da consulta URI (incluindo valores de decodificação e multiparâmetros)

Comentários

Não fiquei satisfeito com o código fornecido pelo @ Pr0gr4mm3r em https://stackoverflow.com/a/13592567/1211082 . A solução baseada em fluxo não faz URLDecoding, a versão mutável grosseira.

Assim, elaborei uma solução que

  • Pode decompor uma parte da consulta URI em um Map<String, List<Optional<String>>>
  • Pode manipular vários valores para o mesmo nome de parâmetro
  • Pode representar parâmetros sem um valor corretamente (em Optional.empty()vez de null)
  • Decodifica nomes e valores de parâmetros corretamente viaURLdecode
  • É baseado no Java 8 Streams
  • É diretamente utilizável (consulte o código, incluindo as importações abaixo)
  • Permite o tratamento adequado de erros (aqui, transformando uma exceção verificada UnsupportedEncodingExceptionem uma exceção de tempo de execução RuntimeUnsupportedEncodingExceptionque permite a interação com o fluxo. (Agrupar função regular em funções que lançam exceções verificadas é uma chatice. E Scala Trynão está disponível no padrão da linguagem Java).

Código 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;
        }
    }
}

Código Scala

... e por uma questão de integridade, não resisto a fornecer a solução em Scala que domina a concisão e a beleza

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
fonte
1

Apenas uma atualização para a versão 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())));
}

Os métodos mapping e toList () devem ser usados ​​com coletores, o que não foi mencionado na resposta superior. Caso contrário, isso geraria erro de compilação no IDE

Aarish Ramesh
fonte
Parece que você também precisa compartilhar seu splitQueryParameters()método? E o que há com o **Collectors**?
22919 Kirby
1

Resposta de Kotlin com referência inicial de https://stackoverflow.com/a/51024552/3286489 , mas com versão aprimorada, ordenando códigos e fornecendo 2 versões, e use operações de coleta imutáveis

Use java.net.URIpara extrair a consulta. Em seguida, use as funções de extensão fornecidas abaixo

  1. Supondo que você queira apenas o último valor da consulta, ou seja page2&page3, obterá {page=3}, use a função de extensão abaixo
    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. Supondo que você queira uma lista de todo o valor da consulta, ou seja page2&page3, obterá{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"

A maneira de usá-lo como abaixo

    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
fonte
1

O Netty também fornece um bom analisador de cadeias de consulta chamado QueryStringDecoder. Em uma linha de código, ele pode analisar o URL na pergunta. Eu gosto porque não requer pegar ou jogar java.net.MalformedURLException.

Em uma linha:

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

Consulte javadocs aqui: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Aqui está um exemplo curto, independente e correto:

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

o que gera

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

Respondendo aqui, porque este é um tópico popular. Esta é uma solução limpa no Kotlin que usa a UrlQuerySanitizerAPI recomendada . Veja a documentação oficial . Eu adicionei um construtor de strings para concatenar e exibir os parâmetros.

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

Aqui está minha solução com reduzir e opcional :

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 parâmetros duplicados (pego o último).
  • Eu uso Optional<SimpleImmutableEntry<String, String>>para ignorar o lixo mais tarde
  • A redução começa com um mapa vazio e preenche-o em cada SimpleImmutableEntry

Caso você pergunte, o reduzir exige esse estranho combinador no último parâmetro, que é usado apenas em fluxos paralelos. Seu objetivo é mesclar dois resultados intermediários (aqui HashMap).

Mike Dudley
fonte
-1

Se você estiver usando o Spring, adicione um argumento do tipo @RequestParam Map<String,String> ao seu método de controlador, e o Spring construirá o mapa para você!

mancini0
fonte