Analysieren Sie eine URI-Zeichenfolge in die Name-Wert-Sammlung


273

Ich habe die URI wie folgt:

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 analysierten Elementen:

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

Um genau zu sein, benötige ich ein Java-Äquivalent für die C # /. NET HttpUtility.ParseQueryString- Methode.

Bitte geben Sie mir einen Rat dazu.

Vielen Dank.



@ MattBall Wenn das OP Android verwendet, dann ist es
Juan Mendes

156
Ist es nicht umwerfend, dass dies nicht Teil der Kern-API von java.net.URI/ ist java.net.URL?
Dilum Ranatunga

Bitte überprüfen Sie diese Lösung - solide Bibliothek und Arbeitsbeispiel für Parse-
arcseldon

Antworten:


341

Wenn Sie nach einer Möglichkeit suchen, dies ohne Verwendung einer externen 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 Karte zugreifen, indem Sie <map>.get("client_id")mit der in Ihrer Frage angegebenen URL "SS" zurückgeben.

UPDATE URL-Decodierung hinzugefügt

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

UPDATE Java8-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 Karte zurück:

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

13
Sie vergessen, die Namen und Parameter zu dekodieren. Ein Grund, warum es normalerweise besser ist, Bibliotheken allgemeine Aufgaben ausführen zu lassen.
Juan Mendes

10
Sie haben zwar Recht ... aber ich persönlich bevorzuge es, solche "einfachen" Aufgaben selbst zu schreiben, anstatt für jede einzelne Aufgabe, die ich zu erledigen habe, eine eigene Bibliothek zu verwenden.
Pr0gr4mm3r

2
Wenn Sie mehrere Parameter mit demselben Namen / Schlüssel haben, überschreibt die Verwendung dieser Funktion den Wert mit einem ähnlichen Schlüssel.
Schneeball147

4
@Chris Sie verwechseln XML / HTML mit der URL-Codierung. Ihre Beispiel-URL sollte lauten: a.com/q?1=a%26b&2=b%26c
sceaj

3
Es wäre schön anzugeben, welche Funktionen verwendet werden: Collectors.mapping (...) und Collectors.toList (...)
Thomas Rebele

311

org.apache.http.client.utils.URLEncodedUtils

ist eine bekannte Bibliothek, die dies 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

Kann ich den Wert anhand seines Namens erhalten, ohne alle Elemente zu übergeben? Ich meine so etwas wie: System.out.print (params ["one"]);
Sergey Shafiev

3
@SergeyShafiev Es ist trivial, ein List<NameValuePair>in ein Map<String,String>Java zu konvertieren, das keinen Klammerzugriff für Hash-Maps hat. Es sieht so aus, als ob map.get("one")wenn Sie nicht wissen, wie das geht, es eine andere Frage sein sollte (aber versuchen Sie es zuerst selbst). . Wir ziehen es vor, die Fragen hier bei SO
Juan Mendes

6
Achten Sie darauf, dass URLEncodedUtils eine IllegalArgumentException
Crystark am

10
@Crystark Ab httpclient 4.3.3 löst eine Abfragezeichenfolge mit doppelten Namen keine Ausnahmen aus. Es funktioniert wie erwartet. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));druckt [foo = bar, foo = baz] .
Akihiro HARAI

4
Ab Android 6 wurde die Apache HTTP-Clientbibliothek entfernt. Dies bedeutet, dass URLEncodedUtils and NameValuePair` nicht mehr verfügbar sind (es sei denn, Sie fügen der alten Apache-Bibliothek eine Abhängigkeit hinzu, wie hier beschrieben ).
Ted Hopp

108

Wenn Sie Spring Framework verwenden:

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

Sie erhalten:

param1: ab
param2: cd,ef

1
für URLs verwendenUriComponentsBuilder.fromHttpUrl(url)
Lu55

51

Verwenden Sie Google Guava und machen Sie es in 2 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}

18
Was ist mit der in der ausgewählten Antwort beschriebenen URL-Dekodierung?
Clint Eastwood

7
Dies gilt auch für mehrere Schlüssel mit demselben Namen. Laut den Javadocs wird dies eine IllegalArgumentException auslösen
jontro

5
Anstatt manuell zu teilen uri, sollten Sie verwenden, new java.net.URL(uri).getQuery()da dies Ihnen eine kostenlose Eingabevalidierung für die URL ermöglicht.
avgvstvs

1
Zum Dekodieren: final Map <String, String> queryVars = Maps.transformValues ​​(Map, neue Funktion <String, String> () {@Override public String apply (String-Wert) {try {return URLDecoder.decode (Wert, "UTF- 8 ");} catch (UnsupportedEncodingException e) {// TODO Automatisch generierter catch-Block e.printStackTrace ();} Rückgabewert;}});
Phreakhead

11
WARNUNG!! Dies ist NICHT sicher, da dies ausgelöst splitter.split()wird, IllegalArgumentExceptionwenn die Abfragezeichenfolge einen doppelten Schlüssel enthält. Siehe stackoverflow.com/questions/1746507/…
Anderson

31

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

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

UPDATE: UriComponentsBuilder kommt aus dem Frühling. Hier der Link .


3
Ohne zu wissen, woher diese UriComponentsBuilder-Klasse stammt, ist sie nicht sehr nützlich.
Thomas Mortagne

3
Es kann erwähnenswert sein, dass dies nur dann eine gute Idee ist, wenn Sie Spring bereits verwenden. Wenn Sie Spring nicht verwenden, sollten Sie dies vermeiden. samatkinson.com/why-i-hate-spring
Nick

1
NB Dies erfordert URIs. Javas Version von URIs ist keine Obermenge von URLs (aus diesem Grund kann toURI Ausnahmen auslösen).
Adam Gent

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

HttpUrl ist ein komischer Name, aber genau das brauchte ich. Vielen Dank.
GuiSim

10

Java 8 eine Anweisung

Angesichts 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 sammelt andererseits eine Karte (vorausgesetzt, dass in einer URL mehr Parameter mit demselben Namen, aber unterschiedlichen Werten vorhanden sein können).

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 Utility-Funktion verwenden, um die Parameter ordnungsgemäß zu dekodieren.

private static String decode(final String encoded) {
    try {
        return encoded == null ? null : URLDecoder.decode(encoded, "UTF-8");
    } catch(final UnsupportedEncodingException e) {
        throw new RuntimeException("Impossible: UTF-8 is a required encoding", e);
    }
}

4
Dies ist eher ein Java 8- Ansatz als ein Java 8-Oneliner.
Stephan

@Stephan gut :) vielleicht beides. Aber ich bin mehr daran interessiert zu verstehen, ob Ihnen diese Lösung gefällt.
freedev

3
IMO, ein Oneliner sollte kurz sein und sich nicht über mehrere Zeilen erstrecken.
Stephan

1
Hier gibt es mehrere Aussagen.
Stephan

2
Ich denke, Sie könnten eine ganze Klasse in einer einzigen Zeile schreiben, aber das ist normalerweise nicht mit dem Ausdruck "Einzeiler" gemeint.
Abhijit Sarkar

10

Wenn Sie das Servlet doGet verwenden, versuchen Sie dies

request.getParameterMap()

Gibt eine java.util.Map der Parameter dieser Anforderung zurück.

Rückgabe: 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 )


Dies funktioniert sowohl mit Spring Web als auch in Ihrem Controller. Sie können einen Parameter vom Typ haben HttpServletRequestund es funktioniert auch mit MockHttpServletRequestMock MVC-Komponententests.
GameSalutes

8

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.


3

Unter Android gibt es eine Uri-Klasse im Paket android.net . Beachten Sie, dass Uri Teil von android.net ist , während URI Teil von java.net ist .

Die Uri-Klasse verfügt über viele Funktionen zum Extrahieren von Schlüssel-Wert-Paaren aus einer Abfrage. Geben Sie hier die Bildbeschreibung ein

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
    }

2

Mit den oben genannten Kommentaren und Lösungen speichere ich alle Abfrageparameter mithilfe von Map <String, Object>, wobei Objekte entweder String oder Set <String> sein können. Die Lösung ist unten angegeben. Es wird empfohlen, zuerst eine Art URL-Validator zu verwenden, um die URL zu validieren, und dann die Methode convertQueryStringToMap 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

Ich habe eine Kotlin-Version ausprobiert und gesehen, wie dies das beste Ergebnis in 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")
}

1
Gleiche Anerkennung für stackoverflow.com/users/1203812/matthew-herod 50/50 Aufwand, kann aber nicht mitautorisieren .
Graham Smith

2

Eine gebrauchsfertige Lösung zur Dekodierung des URI-Abfrageteils (inkl. Dekodierung und Multi-Parameter-Werte)

Bemerkungen

Ich war mit dem Code von @ Pr0gr4mm3r in https://stackoverflow.com/a/13592567/1211082 nicht zufrieden . Die Stream-basierte Lösung führt keine URLDecoding durch, die veränderbare Version ist klumpig.

So habe ich eine Lösung ausgearbeitet, die

  • Kann einen URI-Abfrageteil in einen zerlegen Map<String, List<Optional<String>>>
  • Kann mehrere Werte für denselben Parameternamen verarbeiten
  • Kann Parameter ohne Wert richtig darstellen ( Optional.empty()anstelle von null)
  • Dekodiert Parameternamen und Werte korrekt überURLdecode
  • Basiert auf Java 8 Streams
  • Ist direkt verwendbar (siehe Code einschließlich Importe unten)
  • Ermöglicht eine ordnungsgemäße Fehlerbehandlung (hier durch Verwandeln einer aktivierten Ausnahme UnsupportedEncodingExceptionin eine Laufzeitausnahme RuntimeUnsupportedEncodingException, die das Zusammenspiel mit dem Stream ermöglicht. (Das Einschließen der regulären Funktion in Funktionen, die aktivierte Ausnahmen auslösen, ist schmerzhaft. Und Scala Tryist in der Java-Standardeinstellung nicht verfügbar.)

Java Code

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

Scala Code

... und der Vollständigkeit halber kann ich nicht widerstehen, die Lösung in Scala anzubieten, die von Kürze und Schönheit dominiert

import java.net.URLDecoder

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

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

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

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

1

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, was in der oberen Antwort nicht erwähnt wurde. Andernfalls würde ein Kompilierungsfehler in der IDE ausgelöst


Sieht so aus, als müssten Sie auch Ihre splitQueryParameters()Methode teilen ? Und was ist mit dem **Collectors**?
Kirby

1

Kotlins Antwort mit dem ersten Verweis von https://stackoverflow.com/a/51024552/3286489 , jedoch mit verbesserter Version durch Aufräumen von Codes und Bereitstellung von 2 Versionen davon und Verwendung unveränderlicher Erfassungsvorgänge

Verwenden Sie java.net.URIdiese Option, um die Abfrage zu extrahieren. Verwenden Sie dann die unten angegebenen Erweiterungsfunktionen

  1. Angenommen, Sie möchten nur den letzten Wert der Abfrage, dh page2&page3erhalten {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, dh page2&page3erhalten{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 Verwendung wie unten

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

1

Netty bietet auch einen netten Parser für Abfragezeichenfolgen mit dem Namen QueryStringDecoder. In einer Codezeile kann die URL in der Frage analysiert werden. Ich mag, weil es nicht das Fangen oder Werfen erfordert java.net.MalformedURLException.

In einer Zeile:

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

Siehe javadocs hier: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Hier ist ein kurzes, in sich geschlossenes, korrektes 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

Hier antworten, weil dies ein beliebter Thread ist. Dies ist eine saubere Lösung in Kotlin, die die empfohlene UrlQuerySanitizerAPI verwendet. Siehe die offizielle Dokumentation . 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

Hier ist meine Lösung mit Reduzieren 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 Optional<SimpleImmutableEntry<String, String>>ignoriere Müll später
  • Die Verkleinerung beginnt mit einer leeren Karte und wird dann bei jedem SimpleImmutableEntry ausgefüllt

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


-1

Wenn Sie Spring verwenden, fügen Sie @RequestParam Map<String,String> Ihrer Controller-Methode ein Argument vom Typ hinzu , und Spring erstellt die Map für Sie!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.