it-swarm.com.de

Analysieren Sie einen URI-String in die Name-Value-Collection

Ich habe die URI so:

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

Ich brauche eine Sammlung mit geparsten Elementen:

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

Um genau zu sein, brauche ich ein Java-Äquivalent für die C # -HttpUtility.ParseQueryString-Methode .. Bitte geben Sie mir einen Hinweis dazu.

208
Sergey Shafiev

Wenn Sie nach einer Möglichkeit suchen, dies ohne externe Bibliothek zu erreichen, hilft Ihnen der folgende Code.

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

Sie können auf die zurückgegebene Map mit <map>.get("client_id") zugreifen. Die in Ihrer Frage angegebene URL gibt "SS" zurück.

UPDATEURL-Decoding hinzugefügt

UPDATEDa diese Antwort immer noch sehr beliebt ist, habe ich eine verbesserte Version der obigen Methode erstellt, die mehrere Parameter mit demselben Schlüssel und Parameter ohne Wert behandelt.

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

UPDATEJava8-Version

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

Ausführen der obigen Methode mit der URL 

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

gibt diese Map zurück:

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

org.Apache.http.client.utils.URLEncodedUtils

ist eine bekannte Bibliothek, die es für Sie tun kann

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

Ausgänge 

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

Hinweis: Funktioniert nur in HTTP PUT oder HTTP POST

259
Juan Mendes

Wenn Sie Spring Framework verwenden:

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

Sie erhalten:

param1: abc
param2: def,ghi
64
xxg

verwenden Sie Google Guava und machen Sie es in zwei Zeilen:

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

was gibt dir

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

Der kürzeste Weg, den ich gefunden habe, ist dieser:

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

UPDATE:UriComponentsBuilder stammt von Spring. Hier der Link .

18

Für Android, wenn Sie OkHttp in Ihrem Projekt verwenden. Sie könnten einen Blick darauf werfen. Es ist einfach und hilfreich. 

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

Wenn Sie Java 8 verwenden und bereit sind, einige wiederverwendbare Methoden zu schreiben, können Sie dies in einer Zeile tun.

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

Aber das ist eine ziemlich brutale Linie.

8
Fuwjax

Java 8 eine Anweisung

In Anbetracht der zu analysierenden URL:

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

Diese Lösung sammelt eine Liste von Paaren:

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

Diese Lösung dagegen sammelt eine Map (vorausgesetzt, dass es in einer URL mehr Parameter mit demselben Namen, aber unterschiedlichen Werten gibt).

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

Beide Lösungen müssen eine Dienstprogrammfunktion verwenden, um die Parameter richtig zu decodieren.

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

Wenn Sie Servlet doGet verwenden, versuchen Sie dies

request.getParameterMap()

Gibt eine Java.util.Map der Parameter dieser Anfrage zurück.

Kehrt zurück: eine unveränderliche Java.util.Map, die Parameternamen als Schlüssel und Parameterwerte als Kartenwerte enthält. Die Schlüssel in der Parameterzuordnung sind vom Typ String. Die Werte in der Parameterzuordnung sind vom Typ String-Array.

( Java-Dokument )

5
Ansar Ozden

Unter Verwendung der oben genannten Kommentare und Lösungen speichere ich alle Abfrageparameter mit Map <String, Object>, wobei Objects entweder String oder Set <String> sein kann. Die Lösung ist unten angegeben. Es wird empfohlen, eine Art URL-Validator zu verwenden, um die URL zuerst zu überprüfen und anschließend die convertQueryStringToMap-Methode aufzurufen. 

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

Ich habe mir eine Kotlin-Version vorgenommen, um zu sehen, wie dies das beste Ergebnis bei Google ist.

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

Und die Erweiterungsmethoden

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

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

Nur ein Update auf die Java 8-Version

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

mapping- und toList () -Methoden müssen mit Collectors verwendet werden, die in der Antwort nicht erwähnt wurden. Andernfalls würde ein Kompilierungsfehler in IDE ausgelöst

2
Aarish Ramesh

Netty bietet auch einen Nice Query String Parser namens QueryStringDecoder. In einer Codezeile kann die URL in der Frage analysiert werden. Ich mag es, weil es nicht erforderlich ist, Java.net.MalformedURLException zu fangen oder zu werfen.

In einer Zeile:

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

Sehen Sie Javadocs hier: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Hier ist ein kurzes, eigenständiges, richtiges Beispiel:

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

was erzeugt

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

Antworten hier, weil dies ein beliebter Thread ist. Dies ist eine saubere Lösung in Kotlin, die die empfohlene UrlQuerySanitizer-API verwendet. Siehe die offiziellen Unterlagen . Ich habe einen String-Builder hinzugefügt, um die Parameter zu verketten und anzuzeigen. 

    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

Hier ist meine Lösung mit verkleinern und optional:

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;
}
  • Ich ignoriere doppelte Parameter (ich nehme den letzten).
  • Ich benutze Optional<SimpleImmutableEntry<String, String>>, um den Müll später zu ignorieren
  • Die Reduzierung beginnt mit einer leeren Karte und füllt sie dann bei jedem SimpleImmutableEntry auf

Falls Sie fragen, Verkleinern erfordert diesen seltsamen Combiner im letzten Parameter, der nur in parallelen Streams verwendet wird. Ziel ist es, zwei Zwischenergebnisse (hier HashMap) zusammenzuführen.

0
Mike Dudley

Kotlins Antwort mit anfänglichem Verweis von https://stackoverflow.com/a/51024552/3286489 , aber mit verbesserter Version durch Aufräumen von Codes und Bereitstellung von 2 Versionen davon und Verwendung unveränderlicher Erfassungsoperationen

Verwenden Sie Java.net.URI, um die Abfrage zu extrahieren. Verwenden Sie dann die unten angegebenen Erweiterungsfunktionen

  1. Angenommen, Sie möchten nur den letzten Wert der Abfrage, d. H. page2&page3, erhalten {page=3}, verwenden Sie die folgende Erweiterungsfunktion
    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. Angenommen, Sie möchten eine Liste aller Werte für die Abfrage, d. H. page2&page3, erhalten {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"

Die Art und Weise, es wie folgt zu verwenden

    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

Unter Android gibt es die Uri-Klasse im PaketAndroid.net. Beachten Sie, dass Uri Teil von Android.net ist, während URI Teil von Java.net ist.

Die Uri-Klasse hat viele Funktionen zum Extrahieren von Schlüssel-Wert-Paaren.  enter image description here 

Die folgende Funktion gibt Schlüssel-Wert-Paare in Form von HashMap zurück.

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

In 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