Kennen Sie eine Dienstprogrammklasse / -bibliothek, die Map in eine URL-freundliche Abfragezeichenfolge konvertieren kann?
Beispiel:
Ich habe eine Karte:
"param1"=12,
"param2"="cat"
Ich möchte bekommen:
param1=12¶m2=cat
endgültige Ausgabe
relativeUrl+param1=12¶m2=cat
java
jakarta-ee
utilities
Ula Krukar
quelle
quelle
Antworten:
Die robusteste, die ich von der Stange gesehen habe, ist die URLEncodedUtils- Klasse von Apache Http Compoments (HttpClient 4.0).
Die Methode
URLEncodedUtils.format()
ist genau das, was Sie brauchen.Es wird keine Karte verwendet, sodass Sie doppelte Parameternamen haben können, wie z.
a=1&a=2&b=3
Nicht, dass ich diese Art der Verwendung von Parameternamen empfehle.
quelle
Collections2.transform(paramMap.entrySet(), function)
wo die Funktion einen Map.Entry verwendet und BasicNameValuePair zurückgibt. (Oder machen Sie dasselbe in einfachem altem Java ohne Google Collections.) Zugegeben, es werden ungefähr 6 zusätzliche Zeilen eigenen Codes benötigt.Hier ist etwas, das ich schnell geschrieben habe; Ich bin sicher, dass es verbessert werden kann.
import java.util.*; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; public class MapQuery { static String urlEncodeUTF8(String s) { try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new UnsupportedOperationException(e); } } static String urlEncodeUTF8(Map<?,?> map) { StringBuilder sb = new StringBuilder(); for (Map.Entry<?,?> entry : map.entrySet()) { if (sb.length() > 0) { sb.append("&"); } sb.append(String.format("%s=%s", urlEncodeUTF8(entry.getKey().toString()), urlEncodeUTF8(entry.getValue().toString()) )); } return sb.toString(); } public static void main(String[] args) { Map<String,Object> map = new HashMap<String,Object>(); map.put("p1", 12); map.put("p2", "cat"); map.put("p3", "a & b"); System.out.println(urlEncodeUTF8(map)); // prints "p3=a+%26+b&p2=cat&p1=12" } }
quelle
Map<String, List<String>>
oder dargestelltMap<String, String[]>
. Sie können nämlich mehrere Werte für denselben Namen haben. Ich bin mir nicht sicher, ob OP klug war, es so zu gestalten.null
Schlüsseln / Werten umgehen ? Im Moment dietoString()
UrsachenNullPointerException
, und ich begründe es damit, dass ich "ausfallsicher" sage, aber ich bin mir nicht sicher, wie das mit Leuten gehen würde, die nur wollen, dass die Dinge "funktionieren".Ich fand eine glatte Lösung mit Java 8 und Polygenelubricants 'Solution.
parameters.entrySet().stream() .map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue())) .reduce((p1, p2) -> p1 + "&" + p2) .orElse("");
quelle
?
an den von diesem zurückgegebenen String anzuhängen und an meine URL anzuhängentoto[]=4&toto[]=5
In Spring Util gibt es einen besseren Weg ..,
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>(); params.add("key", key); params.add("storeId", storeId); params.add("orderId", orderId); UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("http://spsenthil.com/order").queryParams(params).build(); ListenableFuture<ResponseEntity<String>> responseFuture = restTemplate.getForEntity(uriComponents.toUriString(), String.class);
quelle
UriComponentsBuilder
UriComponentsBuilder.fromHttpUrl
. Als ich hinzufügteparams.values().removeIf(entry -> entry.getValue() == null);
, funktionierte es nicht. Weiß jemand, warum ich Einträge mit der obigen Syntax nicht aus einer MultiValueMap entfernen kann?Map
inString
einUriComponents
Objekt umgewandelt werden soll , nicht in ein Objekt. Dies ist jedoch nützlich für Personen, die nur die http-Anfrage stellen möchten.Update Juni 2016
Ich fühlte mich gezwungen, eine Antwort hinzuzufügen, nachdem ich viel zu viele SOF-Antworten mit datierten oder unzureichenden Antworten auf sehr häufige Probleme gesehen hatte - eine gute Bibliothek und eine solide Beispielverwendung für beide
parse
undformat
Operationen.Verwenden Sie die Bibliothek org.apache.httpcomponents.httpclient . Die Bibliothek enthält das Dienstprogramm org.apache.http.client.utils.URLEncodedUtils .
Zum Beispiel ist es einfach, diese Abhängigkeit von Maven herunterzuladen:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency>
Für meine Zwecke musste ich nur
parse
(von Abfragezeichenfolge zu Name-Wert-Paarenformat
lesen ) und (von Name-Wert-Paaren zu Abfragezeichenfolge lesen) Abfragezeichenfolgen. Es gibt jedoch Äquivalente, um dasselbe mit einem URI zu tun (siehe auskommentierte Zeile unten).// Erforderliche Importe
import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets;
// Code-Auszug
public static void parseAndFormatExample() throws UnsupportedEncodingException { final String queryString = "nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk"; System.out.println(queryString); // => nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk final List<NameValuePair> params = URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8); // List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), "UTF-8"); for (final NameValuePair param : params) { System.out.println(param.getName() + " : " + param.getValue()); // => nonce : 12345 // => redirectCallbackUrl : http://www.bbc.co.uk } final String newQueryStringEncoded = URLEncodedUtils.format(params, StandardCharsets.UTF_8); // decode when printing to screen final String newQueryStringDecoded = URLDecoder.decode(newQueryStringEncoded, StandardCharsets.UTF_8.toString()); System.out.println(newQueryStringDecoded); // => nonce=12345&redirectCallbackUrl=http://www.bbc.co.uk }
Diese Bibliothek hat genau das getan, was ich brauchte, und konnte gehackten benutzerdefinierten Code ersetzen.
quelle
Wenn Sie tatsächlich einen vollständigen URI erstellen möchten, versuchen Sie URIBuilder von Apache Http Compoments (HttpClient 4).
Dies beantwortet die Frage nicht wirklich, aber es beantwortet die Frage, die ich hatte, als ich diese Frage fand.
quelle
Ich wollte auf der Antwort von @ eclipse mit Java 8-Mapping und -Reduzierung aufbauen.
protected String formatQueryParams(Map<String, String> params) { return params.entrySet().stream() .map(p -> p.getKey() + "=" + p.getValue()) .reduce((p1, p2) -> p1 + "&" + p2) .map(s -> "?" + s) .orElse(""); }
Die zusätzliche
map
Operation nimmt die reduzierte Zeichenfolge und setzt?
nur dann eine vor, wenn die Zeichenfolge vorhanden ist.quelle
Eine andere "eine Klasse" / keine Abhängigkeitsmethode, die mit Single / Multiple umgeht:
import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; public class UrlQueryString { private static final String DEFAULT_ENCODING = "UTF-8"; public static String buildQueryString(final LinkedHashMap<String, Object> map) { try { final Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); final StringBuilder sb = new StringBuilder(map.size() * 8); while (it.hasNext()) { final Map.Entry<String, Object> entry = it.next(); final String key = entry.getKey(); if (key != null) { sb.append(URLEncoder.encode(key, DEFAULT_ENCODING)); sb.append('='); final Object value = entry.getValue(); final String valueAsString = value != null ? URLEncoder.encode(value.toString(), DEFAULT_ENCODING) : ""; sb.append(valueAsString); if (it.hasNext()) { sb.append('&'); } } else { // Do what you want...for example: assert false : String.format("Null key in query map: %s", map.entrySet()); } } return sb.toString(); } catch (final UnsupportedEncodingException e) { throw new UnsupportedOperationException(e); } } public static String buildQueryStringMulti(final LinkedHashMap<String, List<Object>> map) { try { final StringBuilder sb = new StringBuilder(map.size() * 8); for (final Iterator<Entry<String, List<Object>>> mapIterator = map.entrySet().iterator(); mapIterator.hasNext();) { final Entry<String, List<Object>> entry = mapIterator.next(); final String key = entry.getKey(); if (key != null) { final String keyEncoded = URLEncoder.encode(key, DEFAULT_ENCODING); final List<Object> values = entry.getValue(); sb.append(keyEncoded); sb.append('='); if (values != null) { for (final Iterator<Object> listIt = values.iterator(); listIt.hasNext();) { final Object valueObject = listIt.next(); sb.append(valueObject != null ? URLEncoder.encode(valueObject.toString(), DEFAULT_ENCODING) : ""); if (listIt.hasNext()) { sb.append('&'); sb.append(keyEncoded); sb.append('='); } } } if (mapIterator.hasNext()) { sb.append('&'); } } else { // Do what you want...for example: assert false : String.format("Null key in query map: %s", map.entrySet()); } } return sb.toString(); } catch (final UnsupportedEncodingException e) { throw new UnsupportedOperationException(e); } } public static void main(final String[] args) { // Examples: could be turned into unit tests ... { final LinkedHashMap<String, Object> queryItems = new LinkedHashMap<String, Object>(); queryItems.put("brand", "C&A"); queryItems.put("count", null); queryItems.put("misc", 42); final String buildQueryString = buildQueryString(queryItems); System.out.println(buildQueryString); } { final LinkedHashMap<String, List<Object>> queryItems = new LinkedHashMap<String, List<Object>>(); queryItems.put("usernames", new ArrayList<Object>(Arrays.asList(new String[] { "bob", "john" }))); queryItems.put("nullValue", null); queryItems.put("misc", new ArrayList<Object>(Arrays.asList(new Integer[] { 1, 2, 3 }))); final String buildQueryString = buildQueryStringMulti(queryItems); System.out.println(buildQueryString); } } }
Sie können bei Bedarf entweder einfach (in den meisten Fällen einfacher zu schreiben) oder mehrfach verwenden. Beachten Sie, dass beide durch Hinzufügen eines kaufmännischen Und kombiniert werden können ... Wenn Sie Probleme haben, lassen Sie es mich in den Kommentaren wissen.
quelle
Dies ist die Lösung, die ich mit Java 8 und implementiert habe
org.apache.http.client.URLEncodedUtils
. Es ordnet die Einträge der Karte einer Liste von zuBasicNameValuePair
und verwendet dann ApachesURLEncodedUtils
, um daraus eine Abfragezeichenfolge zu machen.List<BasicNameValuePair> nameValuePairs = params.entrySet().stream() .map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); URLEncodedUtils.format(nameValuePairs, Charset.forName("UTF-8"));
quelle
compile 'org.apache.httpcomponents:httpclient:4.5.2'
Verwenden von EntrySet und Streams:
map .entrySet() .stream() .map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining("&"));
quelle
Sie können a
Stream
dafür verwenden, aber anstatt selbst Abfrageparameter anzuhängen, würde ich a verwendenUri.Builder
. Zum Beispiel:final Map<String, String> map = new HashMap<>(); map.put("param1", "cat"); map.put("param2", "12"); final Uri uri = map.entrySet().stream().collect( () -> Uri.parse("relativeUrl").buildUpon(), (builder, e) -> builder.appendQueryParameter(e.getKey(), e.getValue()), (b1, b2) -> { throw new UnsupportedOperationException(); } ).build(); //Or, if you consider it more readable... final Uri.Builder builder = Uri.parse("relativeUrl").buildUpon(); map.entrySet().forEach(e -> builder.appendQueryParameter(e.getKey(), e.getValue()) final Uri uri = builder.build(); //... assertEquals(Uri.parse("relativeUrl?param1=cat¶m2=12"), uri);
quelle
Ich denke, dies ist besser für die Speichernutzung und Leistung, und ich möchte nur den Eigenschaftsnamen senden, wenn der Wert null ist.
public static String toUrlEncode(Map<String, Object> map) { StringBuilder sb = new StringBuilder(); map.entrySet().stream() .forEach(entry -> (entry.getValue() == null ? sb.append(entry.getKey()) : sb.append(entry.getKey()) .append('=') .append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8))) .append('&') ); sb.delete(sb.length() - 1, sb.length()); return sb.toString(); }
quelle
In Java ist nichts eingebaut, um dies zu tun. Aber, hey, Java ist eine Programmiersprache, also ... programmieren wir es!
map.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"))
Dies gibt Ihnen "param1 = 12 & param2 = cat". Jetzt müssen wir die URL und dieses Bit zusammenfügen. Sie würden denken, Sie können einfach tun:
URL + "?" + theAbove
Wenn die URL jedoch bereits ein Fragezeichen enthält, müssen Sie stattdessen alles mit "&" verbinden. Eine Möglichkeit, dies zu überprüfen, besteht darin, festzustellen, ob die URL bereits irgendwo ein Fragezeichen enthält.Außerdem weiß ich nicht genau, was auf Ihrer Karte steht. Wenn es sich um Rohstoffe handelt, müssen Sie den Anruf an
e.getKey()
unde.getValue()
mitURLEncoder.encode
oder ähnlichem wahrscheinlich absichern .Ein weiterer Weg ist, dass Sie eine breitere Sichtweise haben. Versuchen Sie, den Inhalt einer Karte an eine URL anzuhängen, oder ... versuchen Sie, eine HTTP (S) -Anforderung von einem Java-Prozess mit den Inhalten in der Karte als (zusätzliche) HTTP-Parameter zu stellen? Im letzteren Fall können Sie in eine http-Bibliothek wie OkHttp schauen, die einige nette APIs hat, um diesen Job zu erledigen. Dann können Sie zunächst auf die Notwendigkeit verzichten, mit dieser URL herumzuspielen .
quelle
Um die Antwort von @ eclipse ein wenig zu verbessern: In Javaland wird eine Anforderungsparameterzuordnung normalerweise als eine
Map<String, String[]>
, eineMap<String, List<String>>
oder möglicherweise eine Art davon dargestellt,MultiValueMap<String, String>
die ungefähr dasselbe ist. In jedem Fall: Ein Parameter kann normalerweise mehrere Werte haben. Eine Java 8-Lösung wäre daher ungefähr so:public String getQueryString(HttpServletRequest request, String encoding) { Map<String, String[]> parameters = request.getParameterMap(); return parameters.entrySet().stream() .flatMap(entry -> encodeMultiParameter(entry.getKey(), entry.getValue(), encoding)) .reduce((param1, param2) -> param1 + "&" + param2) .orElse(""); } private Stream<String> encodeMultiParameter(String key, String[] values, String encoding) { return Stream.of(values).map(value -> encodeSingleParameter(key, value, encoding)); } private String encodeSingleParameter(String key, String value, String encoding) { return urlEncode(key, encoding) + "=" + urlEncode(value, encoding); } private String urlEncode(String value, String encoding) { try { return URLEncoder.encode(value, encoding); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Cannot url encode " + value, e); } }
quelle
Persönlich würde ich mich für eine Lösung wie diese entscheiden, die der von @rzwitserloot bereitgestellten Lösung unglaublich ähnlich ist, nur subtile Unterschiede.
Diese Lösung ist klein, einfach und sauber. Sie erfordert nur sehr wenig Abhängigkeiten, die alle Teil des Java Util-Pakets sind.
Map<String, String> map = new HashMap<>(); map.put("param1", "12"); map.put("param2", "cat"); String output = "someUrl?"; output += map.entrySet() .stream() .map(x -> x.getKey() + "=" + x.getValue() + "&") .collect(Collectors.joining("&")); System.out.println(output.substring(0, output.length() -1));
quelle
Für eine mehrwertige Karte können Sie wie folgt vorgehen (mit Java 8 Stream APIs)
Die URL-Codierung wurde dabei berücksichtigt.
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); String urlQueryString = params.entrySet() .stream() .flatMap(stringListEntry -> stringListEntry.getValue() .stream() .map(s -> UriUtils.encode(stringListEntry.getKey(), StandardCharsets.UTF_8.toString()) + "=" + UriUtils.encode(s, StandardCharsets.UTF_8.toString()))) .collect(Collectors.joining("&"));
quelle
Kotlin
mapOf( "param1" to 12, "param2" to "cat" ).map { "${it.key}=${it.value}" } .joinToString("&")
quelle