Phân tích chuỗi URI thành Bộ sưu tập giá trị tên


273

Tôi đã có URI như thế này:

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

Tôi cần một bộ sưu tập với các yếu tố được phân tích cú pháp:

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

Để chính xác, tôi cần một Java tương đương với phương thức C # /. NET HttpUtility.PudeQueryString .

Xin vui lòng, cho tôi một lời khuyên về điều này.

Cảm ơn.



@MattBall Nếu OP đang sử dụng Android, thì đó là
Juan Mendes

156
Có phải bạn đang lo lắng rằng đây không phải là một phần của API cốt lõi của java.net.URI/ java.net.URL?
Dilum Ranatunga

Vui lòng kiểm tra giải pháp này - thư viện vững chắc và ví dụ hoạt động cho cả hoạt động Parse và Format: stackoverflow.com/a/37744000/1882064
arcseldon

Câu trả lời:


341

Nếu bạn đang tìm cách để đạt được nó mà không cần sử dụng thư viện bên ngoài, đoạn mã sau sẽ giúp bạn.

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

Bạn có thể truy cập Bản đồ được trả về bằng cách sử dụng <map>.get("client_id")URL được cung cấp trong câu hỏi của bạn, điều này sẽ trả về "SS".

CẬP NHẬT URL-Giải mã được thêm vào

CẬP NHẬT Vì câu trả lời này vẫn còn khá phổ biến, tôi đã tạo một phiên bản cải tiến của phương thức ở trên, xử lý nhiều tham số có cùng khóa và tham số không có giá trị.

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

CẬP NHẬT phiên bản 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);
}

Chạy phương thức trên với URL

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

trả về Bản đồ này:

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

13
Bạn đang quên giải mã tên và tham số, một lý do tại sao thường tốt hơn là để thư viện thực hiện các tác vụ phổ biến.
Juan Mendes

10
thực vậy, bạn đúng ... nhưng cá nhân tôi thích tự mình viết những nhiệm vụ "dễ dàng" như vậy, thay vì sử dụng một thư viện riêng cho mỗi nhiệm vụ tôi phải làm.
Pr0gr4mm3r

2
nếu bạn có nhiều tham số có cùng tên / khóa, sử dụng chức năng này sẽ ghi đè giá trị có khóa tương tự.
snowball147

4
@Chris Bạn đang nhầm lẫn xml / html thoát với mã hóa URL. URL ví dụ của bạn phải là: a.com/q?1=a%26b&2=b%26c
sceaj

3
sẽ thật tuyệt khi chỉ ra các chức năng nào được sử dụng: Collector.micking (...) và Collector.toList (...)
Thomas Rebele

311

org.apache.http.client.utils.URLEncodingUtils

là một thư viện nổi tiếng có thể làm điều đó cho bạn

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

Đầu ra

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

Tôi có thể nhận giá trị bằng tên của nó mà không chuyển tất cả các yếu tố không? Ý tôi là một cái gì đó như thế này: System.out.print (params ["one"]);
Serge Shafiev

3
@SergeyShafiev Việc chuyển đổi List<NameValuePair>thành Map<String,String>Java không có quyền truy cập khung cho các bản đồ băm là điều không quan trọng, nó sẽ giống như map.get("one")Nếu bạn không biết cách làm điều đó, thì nên đặt câu hỏi khác (trước tiên hãy thử nó) . Chúng tôi muốn giữ câu hỏi mỏng ở đây tại SO
Juan Mendes

6
Hãy cẩn thận rằng nếu bạn có hai lần cùng một tham số trong URL của mình (nghĩa là? A = 1 & a = 2) URLEncodingUtils sẽ ném IllegalArgumentException
Crystark

10
@Cstallark Kể từ httpclient 4.3.3, chuỗi truy vấn có tên trùng lặp sẽ không đưa ra bất kỳ ngoại lệ nào. Nó hoạt động như mong đợi. System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));sẽ in [foo = bar, foo = baz] .
Akihiro HARAI

4
Kể từ Android 6, thư viện máy khách HTTP Apache đã bị xóa. Điều này có nghĩa là URLEncodedUtils and NameValuePair` không còn khả dụng (trừ khi bạn thêm một phụ thuộc vào thư viện Apache kế thừa như được mô tả ở đây ).
Ted Hopp

108

Nếu bạn đang sử dụng 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));
}

Bạn sẽ nhận được:

param1: ab
param2: cd,ef

1
để sử dụng URLUriComponentsBuilder.fromHttpUrl(url)
Lu55

51

sử dụng google Guava và thực hiện trong 2 dòng:

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

mang đến cho bạn

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

18
Điều gì về giải mã URL được mô tả trong câu trả lời được chọn?
Clint Eastwood

7
Đây cũng là nghi ngờ cho nhiều khóa có cùng tên. Theo javadocs, điều này sẽ tạo ra một IllegalArgumentException
jontro 30/12/14

5
Thay vì chia thủ công, uribạn nên sử dụng new java.net.URL(uri).getQuery()vì điều này mua cho bạn xác thực đầu vào miễn phí trên URL.
avgvstvs

1
Để giải mã: bản đồ cuối cùng <String, String> queryVars = Maps.transformValues ​​(map, new Function <String, String> () {@Override công khai Chuỗi áp dụng (giá trị chuỗi) {thử {return URLDecoder.decode (value, "UTF- 8 ");} Catch (UnsupportedEncodingException e) {// TODO Khối bắt tự động được tạo tự động e.printStackTrace ();} giá trị trả về;}});
phreakhead

11
CẢNH BÁO!! KHÔNG an toàn để làm điều này vì splitter.split()sẽ ném IllegalArgumentExceptionnếu có khóa trùng lặp trong chuỗi truy vấn. Xem stackoverflow.com/questions/1746507/ Mạnh
Anderson

31

Cách ngắn nhất tôi tìm thấy là cách này:

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

CẬP NHẬT: UriComponentsBuilder đến từ mùa xuân. Đây là đường dẫn .


3
Không biết lớp UriComponentsBuilder này đến từ đâu, nó không hữu ích lắm.
Thomas Mortagne

3
Có thể đáng lưu ý rằng đây chỉ là một ý tưởng tốt nếu bạn đã sử dụng Spring. Nếu bạn không sử dụng Spring, bạn sẽ muốn tránh. samatkinson.com/why-i-hate-spring
Nick

1
NB Điều này có URI. Phiên bản URI của Java không phải là siêu URL (đây là lý do tại sao toURI có thể đưa ra ngoại lệ).
Adam Gent

18

Đối với Android, nếu bạn đang sử dụng OkHttp trong dự án của mình. Bạn có thể có một cái nhìn về điều này. Nó đơn giản và hữu ích.

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

HttpUrl là một cái tên kỳ lạ nhưng đây chính xác là những gì tôi cần. Cảm ơn.
GuiSim

10

Java 8 một tuyên bố

Đưa ra URL để phân tích:

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

Giải pháp này thu thập danh sách các cặp:

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

Mặt khác, giải pháp này thu thập một bản đồ (với điều kiện là trong một url có thể có nhiều tham số có cùng tên nhưng giá trị khác nhau).

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

Cả hai giải pháp phải sử dụng một hàm tiện ích để giải mã chính xác các tham số.

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
Đây là một cách tiếp cận Java 8 hơn là một oneliner Java 8.
Stephan

@Stephan tốt :) có thể cả hai. Nhưng tôi quan tâm hơn để hiểu nếu bạn thích giải pháp này.
freedev

3
IMO, một oneliner nên ngắn và không nên trải dài trên nhiều dòng.
Stephan

1
Có nhiều tuyên bố liên quan ở đây.
Stephan

2
Tôi đoán bạn có thể viết cả một lớp trên một dòng, nhưng đó không phải là cụm từ "one-liner" thường có nghĩa.
Abhijit Sarkar

10

Nếu bạn đang sử dụng servlet doGet hãy thử điều này

request.getParameterMap()

Trả về java.util.Map của các tham số của yêu cầu này.

Trả về: một java.util.Map bất biến chứa tên tham số là khóa và giá trị tham số làm giá trị bản đồ. Các khóa trong bản đồ tham số là kiểu String. Các giá trị trong bản đồ tham số là kiểu mảng String.

(Tài liệu Java )


Điều này hoạt động với Spring Web cũng như trong bộ điều khiển của bạn, bạn có thể có một tham số về kiểu HttpServletRequestvà nó cũng hoạt động với MockHttpServletRequestcác thử nghiệm đơn vị Mock MVC.
GameSalutes

8

Nếu bạn đang sử dụng Java 8 và bạn sẵn sàng viết một vài phương thức có thể sử dụng lại, bạn có thể thực hiện nó trong một dòng.

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

Nhưng đó là một dòng khá tàn bạo.


3

Trên Android, có một lớp Uri trong gói android.net . Lưu ý rằng Uri là một phần của android.net , trong khi URI là một phần của java.net .

Lớp Uri có nhiều hàm để trích xuất các cặp khóa-giá trị từ một truy vấn. nhập mô tả hình ảnh ở đây

Hàm sau trả về các cặp khóa-giá trị dưới dạng HashMap.

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

Trong 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

Sử dụng các nhận xét và giải pháp được đề cập ở trên, tôi đang lưu trữ tất cả các tham số truy vấn bằng Map <String, Object> trong đó Đối tượng có thể là chuỗi hoặc Đặt <Chuỗi>. Giải pháp được đưa ra dưới đây. Bạn nên sử dụng một số loại trình xác nhận url để xác thực url trước và sau đó gọi phương thức convertQueryStringToMap.

private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";

public static Map<String, Object> convertQueryStringToMap(String url) throws UnsupportedEncodingException, URISyntaxException {
    List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), DEFAULT_ENCODING_SCHEME);
    Map<String, Object> queryStringMap = new HashMap<>();
    for(NameValuePair param : params){
        queryStringMap.put(param.getName(), handleMultiValuedQueryParam(queryStringMap, param.getName(), param.getValue()));
    }
    return queryStringMap;
}

private static Object handleMultiValuedQueryParam(Map responseMap, String key, String value) {
    if (!responseMap.containsKey(key)) {
        return value.contains(",") ? new HashSet<String>(Arrays.asList(value.split(","))) : value;
    } else {
        Set<String> queryValueSet = responseMap.get(key) instanceof Set ? (Set<String>) responseMap.get(key) : new HashSet<String>();
        if (value.contains(",")) {
            queryValueSet.addAll(Arrays.asList(value.split(",")));
        } else {
            queryValueSet.add(value);
        }
        return queryValueSet;
    }
}

2

Tôi đã có một phiên bản Kotlin để xem đây là kết quả hàng đầu trong 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
}

Và các phương pháp mở rộng

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

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

1
Tín dụng bằng nhau cho stackoverflow.com/users/1203812/matthew-herod nỗ lực 50/50 nhưng không thể đồng tác giả.
Graham Smith

2

Một giải pháp sẵn sàng để sử dụng để giải mã phần truy vấn URI (bao gồm giải mã và nhiều giá trị tham số)

Bình luận

Tôi không hài lòng với mã được cung cấp bởi @ Pr0gr4mm3r trong https://stackoverflow.com/a/13592567/1211082 . Giải pháp dựa trên Luồng không thực hiện URLDecoding, phiên bản có thể thay đổi bị vón cục.

Vì vậy, tôi đã xây dựng một giải pháp mà

  • Có thể phân tách một phần truy vấn URI thành một Map<String, List<Optional<String>>>
  • Có thể xử lý nhiều giá trị cho cùng một tên tham số
  • Có thể biểu diễn các tham số mà không có giá trị đúng ( Optional.empty()thay vì null)
  • Giải mã tên tham sốgiá trị chính xác thông quaURLdecode
  • Được dựa trên Luồng Java 8
  • Có thể sử dụng trực tiếp (xem mã bao gồm nhập khẩu bên dưới)
  • Cho phép xử lý lỗi thích hợp (ở đây qua biến một ngoại lệ kiểm tra UnsupportedEncodingExceptionvào một ngoại lệ runtime RuntimeUnsupportedEncodingExceptioncho phép tương tác với suối. (Bao bì chức năng thường xuyên vào các chức năng ném ngoại lệ kiểm tra là một nỗi đau. Và Scala Trykhông có sẵn trong các mặc định ngôn ngữ Java.)

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

Mã Scala

... và vì mục đích hoàn chỉnh, tôi không thể cưỡng lại việc cung cấp giải pháp trong Scala thống trị bởi sự ngắn gọn và đẹp đẽ

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

Chỉ là bản cập nhật cho phiên bản 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())));
}

các phương thức ánh xạ và toList () phải được sử dụng với Collector không được đề cập trong câu trả lời hàng đầu. Nếu không, nó sẽ ném lỗi biên dịch trong IDE


Hình như bạn cũng cần chia sẻ splitQueryParameters()phương pháp của mình ? Và những gì với **Collectors**?
Kirby

1

Câu trả lời của Kotlin với tham chiếu ban đầu từ https://stackoverflow.com/a/51024552/3286361 , nhưng với phiên bản được cải tiến bằng cách thu dọn mã và cung cấp 2 phiên bản của nó, và sử dụng các hoạt động thu thập bất biến

Sử dụng java.net.URIđể trích xuất Truy vấn. Sau đó sử dụng các chức năng mở rộng được cung cấp dưới đây

  1. Giả sử bạn chỉ muốn giá trị cuối cùng của truy vấn tức là page2&page3sẽ nhận được {page=3}, hãy sử dụng chức năng mở rộng bên dưới
    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. Giả sử bạn muốn có một danh sách tất cả các giá trị cho truy vấn tức là page2&page3sẽ nhận được{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"

Cách sử dụng như dưới đây

    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 cũng cung cấp một trình phân tích cú pháp chuỗi truy vấn đẹp được gọi QueryStringDecoder. Trong một dòng mã, nó có thể phân tích URL trong câu hỏi. Tôi thích bởi vì nó không yêu cầu bắt hoặc ném java.net.MalformedURLException.

Trong một dòng:

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

Xem javadocs tại đây: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

Dưới đây là một ví dụ ngắn gọn, khép kín, chính xác:

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

tạo ra

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

0

Trả lời ở đây vì đây là một chủ đề phổ biến. Đây là một giải pháp sạch trong Kotlin sử dụng UrlQuerySanitizerapi được đề xuất . Xem tài liệu chính thức . Tôi đã thêm một trình xây dựng chuỗi để nối và hiển thị các thông số.

    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

Đây là giải pháp của tôi với giảmTùy chọn :

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;
}
  • Tôi bỏ qua các tham số trùng lặp (tôi lấy cái cuối cùng).
  • Tôi sử dụng Optional<SimpleImmutableEntry<String, String>>để bỏ qua rác sau này
  • Việc giảm bắt đầu với một bản đồ trống, sau đó điền vào mỗi SimpleImmutableEntry

Trong trường hợp bạn yêu cầu, giảm yêu cầu bộ kết hợp kỳ lạ này trong tham số cuối cùng, chỉ được sử dụng trong các luồng song song. Mục tiêu của nó là hợp nhất hai kết quả trung gian (ở đây HashMap).


-1

Nếu bạn đang sử dụng Spring, hãy thêm một đối số kiểu @RequestParam Map<String,String> vào phương thức điều khiển của bạn và Spring sẽ tạo bản đồ cho bạn!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.