Parsing String URI ke dalam Koleksi Nilai-Nama

274

Saya mendapatkan URI seperti ini:

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

Saya perlu koleksi dengan elemen yang diuraikan:

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

Tepatnya, saya memerlukan Java yang setara untuk metode C # /. NET HttpUtility.ParseQueryString .

Tolong, beri saya saran tentang ini.

Terima kasih.

Sergey Shafiev
sumber
1
kemungkinan duplikat string kueri Parsing di Jawa
Matt Ball
@MattBall Jika OP menggunakan Android, maka itu
Juan Mendes
156
Bukankah ini membingungkan bahwa ini bukan bagian dari API inti dari java.net.URI/ java.net.URL?
Dilum Ranatunga
Silakan periksa solusi ini - pustaka padat dan contoh kerja untuk operasi Parse dan Format: stackoverflow.com/a/37744000/1882064
arcseldon

Jawaban:

342

Jika Anda mencari cara untuk mencapainya tanpa menggunakan perpustakaan eksternal, kode berikut akan membantu Anda.

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

Anda dapat mengakses Peta yang dikembalikan menggunakan <map>.get("client_id"), dengan URL yang diberikan dalam pertanyaan Anda ini akan mengembalikan "SS".

UPDATE URL-Decoding ditambahkan

PEMBARUAN Karena jawaban ini masih cukup populer, saya membuat versi yang lebih baik dari metode di atas, yang menangani beberapa parameter dengan kunci yang sama dan parameter tanpa nilai juga.

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

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

Menjalankan metode di atas dengan URL

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

mengembalikan Peta ini:

{param1=["value1"], param2=[null], param3=["value3", null]}
Pr0gr4mm3r
sumber
13
Anda lupa memecahkan kode nama dan parameter, salah satu alasan mengapa biasanya lebih baik membiarkan perpustakaan melakukan tugas-tugas umum.
Juan Mendes
10
memang, Anda benar ... tetapi saya pribadi lebih suka menulis tugas "mudah" seperti itu sendiri, daripada menggunakan perpustakaan sendiri untuk setiap tugas tunggal yang harus saya lakukan.
Pr0gr4mm3r
2
jika Anda memiliki banyak parameter dengan nama / kunci yang sama, menggunakan fungsi ini akan menimpa nilai yang memiliki kunci yang sama.
bola
4
@ Chris Anda membingungkan xml / html melarikan diri dengan penyandian URL. URL contoh Anda harus: a.com/q?1=a%26b&2=b%26c
sceaj
3
alangkah baiknya untuk menunjukkan fungsi mana yang digunakan: Collectors.mapping (...) dan Collectors.toList (...)
Thomas Rebele
311

org.apache.http.client.utils.URLEncodedUtils

adalah perpustakaan terkenal yang dapat melakukannya untuk Anda

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

Keluaran

one : 1
two : 2
three : 3
three : 3a
Juan Mendes
sumber
Bisakah saya menerima nilai dengan namanya tanpa melewati semua elemen? Maksud saya sesuatu seperti ini: System.out.print (params ["one"]);
Sergey Shafiev
3
@SergeyShafiev Sangat sepele untuk mengkonversi List<NameValuePair>menjadi Map<String,String>Java tidak memiliki akses braket untuk peta hash, akan terlihat seperti map.get("one")Jika Anda tidak tahu bagaimana melakukan itu, itu harus menjadi pertanyaan lain (tetapi coba sendiri terlebih dahulu) . Kami lebih suka menyimpan pertanyaan yang ramping di sini di SO
Juan Mendes
6
Hati-hati jika Anda memiliki dua kali parameter yang sama di URL Anda (yaitu? A = 1 & a = 2) URLEncodedUtils akan melempar IllegalArgumentException
Crystark
10
@Crystark Pada httpclient 4.3.3, string kueri dengan nama duplikat tidak memberikan pengecualian. Ini berfungsi seperti yang diharapkan. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));akan mencetak [foo = bar, foo = baz] .
Akihiro HARAI
4
Pada Android 6, pustaka klien HTTP Apache telah dihapus. Ini berarti URLEncodedUtils and NameValuePair` tidak lagi tersedia (kecuali Anda menambahkan ketergantungan ke pustaka Apache lawas seperti dijelaskan di sini ).
Ted Hopp
109

Jika Anda menggunakan 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));
}

Kamu akan mendapatkan:

param1: ab
param2: cd,ef
xxg
sumber
1
untuk penggunaan URLUriComponentsBuilder.fromHttpUrl(url)
Lu55
51

gunakan google Guava dan lakukan dalam 2 baris:

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

yang memberi Anda

{client_id=SS, response_type=code, scope=N_FULL, access_type=offline, redirect_uri=http://localhost/Callback}
Amin Abbaspour
sumber
18
Bagaimana dengan decoding URL yang dijelaskan dalam jawaban yang dipilih?
Clint Eastwood
7
Ini juga diduga beberapa kunci dengan nama yang sama. Menurut javadocs ini akan melempar IllegalArgumentException
jontro
5
Alih-alih membelah secara manual, uriAnda harus menggunakan new java.net.URL(uri).getQuery()karena ini membeli validasi input gratis di URL.
avgvstvs
1
Untuk mendekode: Peta akhir <String, String> queryVars = Maps.transformValues ​​(map, Function baru <String, String> () {@Override String publik berlaku (Nilai string) {coba {return URLDecoder.decode (value, "UTF- 8 ");} catch (UnsupportedEncodingException e) {// TODO blok catch yang dihasilkan secara otomatis e.printStackTrace ();} nilai pengembalian;}});
phreakhead
11
PERINGATAN!! Ini TIDAK aman untuk melakukan ini karena splitter.split()akan melempar IllegalArgumentExceptionjika ada kunci duplikat dalam string kueri. Lihat stackoverflow.com/questions/1746507/…
Anderson
31

Cara terpendek yang saya temukan adalah yang ini:

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

UPDATE: UriComponentsBuilder berasal dari Spring. Sini tautannya .

Dmitry Senkovich
sumber
3
Tanpa mengetahui dari mana kelas UriComponentsBuilder ini berasal, itu tidak terlalu berguna.
Thomas Mortagne
3
Mungkin perlu dicatat bahwa ini hanya ide yang bagus jika Anda sudah menggunakan Spring. Jika Anda tidak menggunakan Spring, Anda harus menghindari. samatkinson.com/why-i-hate-spring
Nick
1
NB Ini membutuhkan URI. URI versi Java bukan superset dari URL (inilah sebabnya toURI dapat memberikan pengecualian).
Adam Gent
18

Untuk Android, jika Anda menggunakan OkHttp dalam proyek Anda. Anda mungkin bisa melihat ini. Sederhana dan bermanfaat.

final HttpUrl url = HttpUrl.parse(query);
if (url != null) {
    final String target = url.queryParameter("target");
    final String id = url.queryParameter("id");
}
THANN Phearum
sumber
HttpUrl adalah jenis nama yang aneh tapi inilah yang saya butuhkan. Terima kasih.
GuiSim
10

Java 8 satu pernyataan

URL yang diberikan untuk dianalisis:

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

Solusi ini mengumpulkan daftar pasangan:

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

Solusi ini di sisi lain mengumpulkan peta (mengingat bahwa dalam url mungkin ada lebih banyak parameter dengan nama yang sama tetapi nilai yang berbeda).

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

Kedua solusi harus menggunakan fungsi utilitas untuk mendekode parameter dengan benar.

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
sumber
4
Ini lebih merupakan pendekatan Java 8 daripada oneliner Java 8.
Stephan
@Stephan well :) mungkin keduanya. Tetapi saya lebih tertarik untuk memahami jika Anda menyukai solusi ini.
freedev
3
IMO, oneliner harus pendek dan tidak boleh menjangkau beberapa baris.
Stephan
1
Ada beberapa pernyataan yang terlibat di sini.
Stephan
2
Saya kira Anda bisa menulis seluruh kelas pada satu baris, tetapi bukan itu yang biasanya dimaksud dengan frasa "satu baris".
Abhijit Sarkar
10

Jika Anda menggunakan servlet doGet, coba ini

request.getParameterMap()

Mengembalikan java.util.Map dari parameter permintaan ini.

Pengembalian: java.util.Map yang tidak dapat diubah berisi nama parameter sebagai kunci dan nilai parameter sebagai nilai peta. Kunci dalam peta parameter adalah tipe String. Nilai-nilai di peta parameter adalah tipe array String.

( Java doc )

Ansar Ozden
sumber
Ini bekerja dengan Spring Web dan juga di controller Anda, Anda dapat memiliki parameter tipe HttpServletRequestdan berfungsi dengan MockHttpServletRequestbaik dalam tes unit Mock MVC.
GameSalutes
8

Jika Anda menggunakan Java 8 dan Anda ingin menulis beberapa metode yang dapat digunakan kembali, Anda dapat melakukannya dalam satu baris.

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

Tapi itu garis yang cukup brutal.

Fuwjax
sumber
3

Di Android, ada kelas Uri dalam paket android.net . Perhatikan bahwa Uri adalah bagian dari android.net , sedangkan URI adalah bagian dari java.net .

Kelas Uri memiliki banyak fungsi untuk mengekstrak pasangan nilai kunci dari sebuah kueri. masukkan deskripsi gambar di sini

Fungsi berikut mengembalikan pasangan nilai kunci dalam bentuk HashMap.

Di Jawa:

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

Di 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
sumber
2

Menggunakan komentar dan solusi yang disebutkan di atas, saya menyimpan semua parameter kueri menggunakan Map <String, Object> di mana Object dapat berupa string atau Set <String>. Solusinya diberikan di bawah ini. Disarankan untuk menggunakan beberapa jenis validator url untuk memvalidasi url terlebih dahulu dan kemudian memanggil metode 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
sumber
2

Saya mencoba versi Kotlin melihat bagaimana ini adalah hasil teratas di 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
}

Dan metode ekstensi

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

fun String.decodeToUTF8(): String { 
    URLDecoder.decode(this, "UTF-8")
}
Graham Smith
sumber
1
Penghargaan yang sama untuk stackoverflow.com/users/1203812/matthew-herod 50/50 upaya tetapi tidak dapat ditulis bersama.
Graham Smith
2

Solusi siap pakai untuk decoding bagian permintaan URI (termasuk decoding dan nilai multi-parameter)

Komentar

Saya tidak senang dengan kode yang diberikan oleh @ Pr0gr4mm3r di https://stackoverflow.com/a/13592567/1211082 . Solusi berbasis streaming tidak melakukan URLDecoding, versi clumpsy yang bisa berubah.

Demikian saya uraikan solusi itu

  • Dapat menguraikan bagian kueri URI menjadi a Map<String, List<Optional<String>>>
  • Dapat menangani beberapa nilai untuk nama parameter yang sama
  • Dapat mewakili parameter tanpa nilai dengan benar ( Optional.empty()bukan null)
  • Menguraikan nama dan nilai parameter dengan benar melaluiURLdecode
  • Didasarkan pada Java 8 Streaming
  • Langsung dapat digunakan (lihat kode termasuk impor di bawah)
  • Memungkinkan penanganan kesalahan yang tepat (di sini, dengan mengubah pengecualian yang diperiksa UnsupportedEncodingExceptionmenjadi pengecualian runtime RuntimeUnsupportedEncodingExceptionyang memungkinkan interaksi dengan aliran. (Membungkus fungsi biasa ke dalam fungsi melempar pengecualian yang diperiksa adalah hal yang menyusahkan. Dan Scala Trytidak tersedia dalam bahasa Jawa standar.)

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

Kode Scala

... dan demi kelengkapan, saya tidak bisa menolak untuk memberikan solusi dalam Scala yang mendominasi oleh singkatnya dan keindahan

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

Hanya pembaruan ke versi 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())));
}

metode pemetaan dan toList () harus digunakan dengan Kolektor yang tidak disebutkan dalam jawaban teratas. Kalau tidak, itu akan membuat kesalahan kompilasi dalam IDE

Aarish Ramesh
sumber
Sepertinya Anda juga perlu membagikan splitQueryParameters()metode Anda ? Dan ada apa dengan itu **Collectors**?
Kirby
1

Jawaban Kotlin dengan referensi awal dari https://stackoverflow.com/a/51024552/3286489 , tetapi dengan versi yang disempurnakan dengan merapikan kode dan menyediakan 2 versi, dan menggunakan operasi pengumpulan yang tidak dapat diubah

Gunakan java.net.URIuntuk mengekstrak Query. Kemudian gunakan fungsi ekstensi yang disediakan di bawah ini

  1. Dengan asumsi Anda hanya menginginkan nilai terakhir dari kueri yang page2&page3akan didapat {page=3}, gunakan fungsi ekstensi di bawah ini
    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. Dengan asumsi Anda menginginkan daftar semua nilai untuk kueri yaitu page2&page3akan mendapatkan{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"

Cara menggunakannya seperti di bawah ini

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

Netty juga menyediakan parser string kueri bagus yang disebut QueryStringDecoder. Dalam satu baris kode, ini dapat mem-parsing URL dalam pertanyaan. Saya suka karena tidak perlu menangkap atau melempar java.net.MalformedURLException.

Dalam satu baris:

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

Lihat javadocs di sini: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Berikut ini adalah contoh singkat, lengkap, benar:

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

yang menghasilkan

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

Menjawab di sini karena ini adalah utas populer. Ini adalah solusi bersih di Kotlin yang menggunakan UrlQuerySanitizerapi yang disarankan . Lihat dokumentasi resmi . Saya telah menambahkan pembangun string untuk menggabungkan dan menampilkan params.

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

Inilah solusi saya dengan mengurangi dan Opsional :

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;
}
  • Saya mengabaikan parameter duplikat (saya ambil yang terakhir).
  • Saya gunakan Optional<SimpleImmutableEntry<String, String>>untuk mengabaikan sampah nanti
  • Pengurangan dimulai dengan peta kosong, lalu isi di setiap SimpleImmutableEntry

Jika Anda bertanya, mengurangi memerlukan penggabung aneh ini di parameter terakhir, yang hanya digunakan dalam aliran paralel. Tujuannya adalah untuk menggabungkan dua hasil antara (di sini HashMap).

Mike Dudley
sumber
-1

Jika Anda menggunakan Spring, tambahkan argumen tipe @RequestParam Map<String,String> ke metode controller Anda, dan Spring akan membuat peta untuk Anda!

mancini0
sumber