แยกสตริง URI ลงในชุดชื่อมูลค่า


274

ฉันมี URI เช่นนี้:

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

ฉันต้องการคอลเล็กชันที่มีองค์ประกอบแยกวิเคราะห์:

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

เป็นที่แน่นอนฉันต้องเทียบเท่า Java สำหรับ C # /. NET HttpUtility.ParseQueryStringวิธี

กรุณาให้คำแนะนำกับฉัน

ขอบคุณ


1
เป็นไปได้ที่ซ้ำกันของการแยกวิเคราะห์สตริงใน Java
Matt Ball

@MattBall หาก OP ใช้ Android ก็เป็นได้
Juan Mendes

156
ไม่น่าเชื่อว่านี่จะไม่เป็นส่วนหนึ่งของ core API ของjava.net.URI/ java.net.URL?
Dilum Ranatunga

โปรดตรวจสอบโซลูชันนี้ - ไลบรารีทึบและตัวอย่างการทำงานสำหรับทั้งการแยกวิเคราะห์และการดำเนินการจัดรูปแบบ: stackoverflow.com/a/37744000/1882064
arcseldon

คำตอบ:


342

หากคุณกำลังมองหาวิธีที่จะทำให้สำเร็จโดยไม่ต้องใช้ไลบรารีภายนอกรหัสต่อไปนี้จะช่วยคุณ

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

คุณสามารถเข้าถึงแผนที่ที่ส่งคืนโดยใช้<map>.get("client_id")URL ที่ให้ไว้ในคำถามของคุณซึ่งจะส่งคืน "SS"

UPDATE URL-ถอดรหัสเพิ่ม

อัปเดตเนื่องจากคำตอบนี้ยังคงเป็นที่นิยมฉันได้ปรับปรุงวิธีการข้างต้นซึ่งจัดการกับพารามิเตอร์หลายตัวด้วยคีย์และพารามิเตอร์เดียวกันโดยไม่มีค่าเช่นกัน

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

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

ใช้วิธีการด้านบนด้วย URL

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

ส่งคืนแผนที่นี้:

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

13
คุณลืมถอดรหัสชื่อและพารามิเตอร์เหตุผลหนึ่งที่ทำให้ห้องสมุดทำงานทั่วไปได้ดีกว่า
Juan Mendes

10
จริง ๆ แล้วคุณเป็นคนถูก ... แต่โดยส่วนตัวแล้วฉันชอบเขียนงาน "ง่าย ๆ " ด้วยตัวเองแทนที่จะใช้ห้องสมุดของตัวเองสำหรับงานทุกงานที่ฉันต้องทำ
Pr0gr4mm3r

2
หากคุณมีหลายพารามิเตอร์ที่มีชื่อ / คีย์เดียวกันการใช้ฟังก์ชันนี้จะแทนที่ค่าที่มีคีย์ที่คล้ายกัน
snowball147

4
@Chris คุณสับสน xml / html escaping ด้วยการเข้ารหัส URL URL ตัวอย่างของคุณควรเป็น: a.com/q?1=a%26b&2=b%26c
sceaj

3
มันจะเป็นการดีที่จะระบุว่าฟังก์ชั่นใดที่ใช้งาน: Collector.mapping (... ) และ Collector.toList (... )
Thomas Rebele

311

org.apache.http.client.utils.URLEncodedUtils

เป็นห้องสมุดที่รู้จักกันดีที่สามารถทำเพื่อคุณ

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

เอาท์พุท

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

ฉันสามารถรับค่าด้วยชื่อโดยไม่ผ่านองค์ประกอบทั้งหมดได้หรือไม่ ฉันหมายถึงสิ่งนี้: System.out.print (params ["one"]);
Sergey Shafiev

3
@SergeyShafiev มันเป็นที่น่ารำคาญในการแปลงList<NameValuePair>เป็นMap<String,String>Java ไม่สามารถเข้าถึงตัวยึดสำหรับแผนที่กัญชาก็จะมีลักษณะเช่นmap.get("one")หากคุณไม่ทราบว่าจะทำอย่างไรที่มันควรจะเป็นคำถามอื่น ( แต่ลองในครั้งแรกของคุณเอง) . เราต้องการที่จะเก็บคำถามไว้ที่นี่ใน SO
Juan Mendes

6
โปรดระวังว่าหากคุณมีพารามิเตอร์เดียวกันสองครั้งใน URL ของคุณ (เช่น? a = 1 & a = 2) URLEncodedUtils จะส่ง IllegalArgumentException ออกมา
Crystark

10
@Crystark ตั้งแต่ httpclient 4.3.3 สตริงข้อความค้นหาที่มีชื่อซ้ำจะไม่มีข้อยกเว้นใด ๆ มันทำงานได้ตามที่คาดไว้ System.out.println(URLEncodedUtils.parse(new URI("http://example.com/?foo=bar&foo=baz"), "UTF-8"));จะพิมพ์[foo = บาร์, foo = baz]
Akihiro HARAI

4
ในฐานะของ Android 6 ไลบรารีไคลเอ็นต์ Apache HTTP ได้ถูกลบออก ซึ่งหมายความว่าURLEncodedUtils and NameValuePair` จะไม่สามารถใช้ได้อีกต่อไป (เว้นแต่คุณจะเพิ่มการอ้างอิงไปยังไลบรารี Apache ดั้งเดิมตามที่อธิบายไว้ที่นี่ )
Ted Hopp

108

หากคุณกำลังใช้ 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));
}

คุณจะได้รับ:

param1: ab
param2: cd,ef

1
สำหรับ URL ที่ใช้UriComponentsBuilder.fromHttpUrl(url)
Lu55

51

ใช้ google Guava และทำใน 2 บรรทัด:

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

ที่ให้คุณ

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

18
สิ่งที่เกี่ยวกับการถอดรหัส URL ที่อธิบายไว้ในคำตอบที่เลือก?
Clint Eastwood

7
นอกจากนี้ยังสงสัยว่าจะมีหลายคีย์ด้วยชื่อเดียวกัน ตาม javadocs นี้จะโยน IllegalArgumentException
jontro

5
แทนที่จะแยกด้วยตนเองuriคุณควรใช้new java.net.URL(uri).getQuery()เช่นนี้ซื้อคุณตรวจสอบอินพุตฟรีใน URL
avgvstvs

1
สำหรับการถอดรหัส: แผนที่สุดท้าย <String, String> queryVars = Maps.transformValues ​​(แผนที่, ฟังก์ชั่นใหม่ <String, String> () {@Override ใช้สตริงสาธารณะ (ค่าสตริง) {ลอง {return URLDecoder.decode (ค่า "UTF- 8 ");} catch (UnsupportedEncodingException e) {// TODO บล็อก catch ที่สร้างขึ้นอัตโนมัติ e.printStackTrace ();} ค่าส่งคืน;}});
phreakhead

11
คำเตือน!! การทำเช่นนี้splitter.split()จะไม่ปลอดภัยเนื่องจากจะเกิดขึ้นIllegalArgumentExceptionหากมีคีย์ซ้ำในสตริงการสืบค้น ดูstackoverflow.com/questions/1746507/…
Anderson

31

วิธีที่สั้นที่สุดที่ฉันพบคืออันนี้:

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

UPDATE: UriComponentsBuilderมาจาก Spring ที่นี่การเชื่อมโยง


3
โดยที่ไม่รู้ว่าคลาส UriComponentsBuilder นี้มาจากไหนมันไม่มีประโยชน์มาก
โทมัสมอร์ตาญ

3
อาจเป็นเรื่องที่น่าสังเกตว่านี่เป็นความคิดที่ดีถ้าคุณใช้ Spring อยู่แล้ว หากคุณไม่ได้ใช้สปริงคุณจะต้องหลีกเลี่ยง samatkinson.com/why-i-hate-spring
นิค

1
NBสิ่งนี้ใช้ URI URIs ของ Java เวอร์ชันไม่ใช่ชุดของ URL (นี่คือเหตุผลที่ toURI สามารถส่งข้อยกเว้นได้)
Adam Gent

18

สำหรับ Android หากคุณใช้OkHttpในโครงการของคุณ คุณอาจได้รับสิ่งนี้ มันง่ายและเป็นประโยชน์

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

HttpUrl เป็นชื่อแปลก ๆ แต่นี่คือสิ่งที่ฉันต้องการ ขอบคุณ
GuiSim

10

Java 8 หนึ่งคำสั่ง

ระบุ 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");

โซลูชันนี้รวบรวมรายการของคู่:

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

ในทางกลับกันการแก้ปัญหานี้รวบรวมแผนที่ (ระบุว่าใน URL อาจมีพารามิเตอร์มากขึ้นด้วยชื่อเดียวกัน แต่ค่าที่แตกต่างกัน)

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

โซลูชันทั้งสองต้องใช้ฟังก์ชันยูทิลิตี้เพื่อถอดรหัสพารามิเตอร์อย่างถูกต้อง

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
นี้เป็นมากขึ้น Java 8 วิธีมากกว่า 8 Java oneliner
เตฟาน

@ สเตฟานดี :) อาจจะทั้งคู่ แต่ฉันสนใจที่จะเข้าใจถ้าคุณชอบวิธีนี้
freedev

3
IMO ผู้เผยแพร่รายควรสั้นและไม่ควรยาวเกินหลายบรรทัด
เตฟาน

1
มีหลายงบที่เกี่ยวข้องที่นี่
Stephan

2
ฉันเดาว่าคุณสามารถเขียนทั้งชั้นในบรรทัดเดียว แต่นั่นไม่ใช่สิ่งที่มักจะหมายถึงวลี "หนึ่งซับ"
Abhijit Sarkar

10

ถ้าคุณใช้ servlet doGet ลองใช้ดู

request.getParameterMap()

ส่งคืน java.util.Map ของพารามิเตอร์ของคำขอนี้

คืนค่า: java.util.Map ที่ไม่เปลี่ยนรูปแบบที่มีชื่อพารามิเตอร์เป็นคีย์และค่าพารามิเตอร์เป็นค่าแผนที่ กุญแจในการแมปพารามิเตอร์เป็นประเภทสตริง ค่าในการแมปพารามิเตอร์เป็นประเภทอาร์เรย์สตริง

( Java doc )


สิ่งนี้ใช้ได้กับ Spring Web และในคอนโทรลเลอร์ของคุณคุณสามารถมีพารามิเตอร์เป็นประเภทHttpServletRequestและใช้ได้กับMockHttpServletRequestการทดสอบหน่วย Mock MVC
GameSalutes

8

หากคุณใช้ Java 8 และคุณยินดีที่จะเขียนวิธีการที่นำมาใช้ซ้ำได้สองสามวิธีคุณสามารถทำได้ในหนึ่งบรรทัด

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

แต่นั่นเป็นสายที่โหดร้าย


3

บน Android มีระดับ Uri ในแพคเกจandroid.net โปรดทราบว่ายูริเป็นส่วนหนึ่งของandroid.netขณะURIเป็นส่วนหนึ่งของjava.net

คลาส Uri มีฟังก์ชันมากมายในการแยกคู่คีย์ - ค่าออกจากแบบสอบถาม ป้อนคำอธิบายรูปภาพที่นี่

ฟังก์ชั่นดังต่อไปนี้จะส่งกลับคู่คีย์ - ค่าในรูปแบบของ HashMap

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

ใน 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

การใช้ความเห็นและวิธีการแก้ไขที่กล่าวถึงข้างต้นฉันกำลังจัดเก็บพารามิเตอร์การสืบค้นทั้งหมดโดยใช้ Map <String, Object> โดยที่ Object สามารถเป็นสตริงหรือ Set <String> การแก้ปัญหาได้รับด้านล่าง ขอแนะนำให้ใช้ตัวตรวจสอบความถูกต้อง URL บางประเภทเพื่อตรวจสอบความถูกต้องของ URL ก่อนแล้วจึงเรียกวิธีการ 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

ฉันไปที่รุ่น Kotlin เพื่อดูว่านี่เป็นผลลัพธ์อันดับต้น ๆ ใน 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
}

และวิธีการขยาย

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

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

1
เครดิตเท่ากันกับstackoverflow.com/users/1203812/matthew-herodความพยายาม 50/50 แต่ไม่สามารถเขียนร่วมได้
เกรแฮมสมิ ธ

2

โซลูชันพร้อมใช้งานสำหรับการถอดรหัสส่วนแบบสอบถาม URI (รวมถึงการถอดรหัสและค่าพารามิเตอร์หลายค่า)

ความคิดเห็น

ผมไม่ได้มีความสุขกับรหัสที่ให้ไว้โดย @ Pr0gr4mm3r ในhttps://stackoverflow.com/a/13592567/1211082 โซลูชันที่ใช้สตรีมไม่ได้ทำ URLDecoding ซึ่งเป็นเวอร์ชันที่ไม่แน่นอน

ดังนั้นฉันจึงอธิบายวิธีแก้ปัญหาที่

  • สามารถแยกส่วนแบบสอบถาม URI ลงใน Map<String, List<Optional<String>>>
  • สามารถจัดการหลายค่าสำหรับชื่อพารามิเตอร์เดียวกัน
  • สามารถแทนค่าพารามิเตอร์โดยไม่มีค่าอย่างถูกต้อง ( Optional.empty()แทนnull)
  • ถอดรหัสชื่อพารามิเตอร์และค่าได้อย่างถูกต้องผ่านURLdecode
  • ขึ้นอยู่กับ Java 8 Streams
  • สามารถใช้งานได้โดยตรง (ดูรหัสรวมถึงการนำเข้าด้านล่าง)
  • ช่วยให้การจัดการข้อผิดพลาดที่เหมาะสม (ที่นี่ผ่านการเปลี่ยนข้อยกเว้นการตรวจสอบUnsupportedEncodingExceptionเป็นข้อยกเว้นรันไทม์RuntimeUnsupportedEncodingExceptionที่ช่วยให้มีปฏิสัมพันธ์กับกระแส. (ตัดฟังก์ชั่นปกติเป็นฟังก์ชั่นการขว้างปาข้อยกเว้นการตรวจสอบเป็นความเจ็บปวด. และสกาล่าTryไม่สามารถใช้ได้ในเริ่มต้นภาษา Java.)

รหัส 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;
        }
    }
}

รหัสสกาล่า

... และเพื่อความสมบูรณ์ฉันไม่สามารถต้านทานเพื่อให้การแก้ปัญหาในสกาล่าที่ครอบงำด้วยความกะทัดรัดและความงาม

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

เพียงแค่อัปเดตเป็นเวอร์ชั่น 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())));
}

การทำแผนที่และ toList () วิธีการจะต้องใช้กับ Collector ซึ่งไม่ได้กล่าวถึงในคำตอบด้านบน มิฉะนั้นจะทำให้เกิดข้อผิดพลาดในการคอมไพล์ใน IDE


ดูเหมือนว่าคุณต้องการแบ่งปันsplitQueryParameters()วิธีการของคุณด้วยหรือไม่ และมี**Collectors**อะไรบ้าง
เคอร์บี้

1

คำตอบของ Kotlin พร้อมการอ้างอิงเริ่มต้นจากhttps://stackoverflow.com/a/51024552/3286489แต่มีรุ่นที่ปรับปรุงแล้วโดยการจัดเรียงรหัสและให้ 2 รุ่นและใช้การดำเนินการรวบรวมที่ไม่เปลี่ยนรูป

ใช้java.net.URIเพื่อแยกแบบสอบถาม จากนั้นใช้ฟังก์ชั่นส่วนต่อขยายที่ให้ไว้ด้านล่าง

  1. สมมติว่าคุณต้องการค่าสุดท้ายของการสืบค้นเช่นpage2&page3จะได้รับ{page=3}ให้ใช้ฟังก์ชันส่วนขยายด้านล่าง
    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. สมมติว่าคุณต้องการรายการค่าทั้งหมดสำหรับแบบสอบถามเช่นpage2&page3จะได้รับ{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"

วิธีการใช้งานดังต่อไปนี้

    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

NettyQueryStringDecoderนอกจากนี้ยังมีตัวแยกวิเคราะห์สตริงแบบสอบถามมีความสุขที่เรียกว่า ในโค้ดหนึ่งบรรทัดสามารถแยกวิเคราะห์ URL ในคำถามได้ java.net.MalformedURLExceptionผมชอบเพราะมันไม่จำเป็นต้องมีการจับหรือการขว้างปา

ในหนึ่งบรรทัด:

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

ดู javadocs ที่นี่: https://netty.io/4.1/api/io/netty/handler/codec/http/QueryStringDecoder.html

นี่คือตัวอย่างย่อที่ถูกต้องในตัวเองและถูกต้อง:

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

ซึ่งสร้าง

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

0

ตอบที่นี่เพราะนี่เป็นกระทู้ยอดนิยม นี่เป็นโซลูชันที่สะอาดใน Kotlin ที่ใช้UrlQuerySanitizerAPI ที่แนะนำ ดูเอกสารอย่างเป็นทางการ ฉันได้เพิ่มตัวสร้างสตริงเพื่อต่อเชื่อมและแสดงพารามิเตอร์

    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

นี่คือทางออกของฉันด้วยการลดและตัวเลือก :

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;
}
  • ฉันไม่สนใจพารามิเตอร์ที่ซ้ำกัน (ฉันใช้พารามิเตอร์สุดท้าย)
  • ฉันใช้Optional<SimpleImmutableEntry<String, String>>เพื่อละเว้นขยะในภายหลัง
  • การลดเริ่มต้นด้วยแผนที่ว่างแล้วเติมลงใน SimpleImmutableEntry แต่ละรายการ

ในกรณีที่คุณขอลดต้องใช้ combiner แปลกนี้ในพารามิเตอร์สุดท้ายซึ่งจะใช้เฉพาะในกระแสข้อมูลแบบขนาน เป้าหมายคือเพื่อรวมผลลัพธ์กลางสองรายการ (ที่นี่ HashMap)


-1

หากคุณใช้ Spring ให้เพิ่มอาร์กิวเมนต์ชนิด@RequestParam Map<String,String> ลงในวิธีการควบคุมของคุณแล้ว Spring จะสร้างแผนที่ให้คุณ!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.