web-dev-qa-db-fra.com

Comment convertir la carte en chaîne de requête url?

Connaissez-vous une classe/bibliothèque d’utilitaires capable de convertir Map en chaîne de requête conviviale pour les URL?

Exemple:

J'ai une carte:

"param1"=12,
"param2"="cat"

Je veux obtenir:

param1=12&param2=cat

sortie finale

relativeUrl+param1=12&param2=cat
63
Ula Krukar

Le plus robuste que j'ai vu sur le marché est le RLEncodedUtils classe de Apache Http Compoments (HttpClient 4.0).

La méthode URLEncodedUtils.format() est ce dont vous avez besoin.

Il n'utilise pas de carte, vous pouvez donc avoir des noms de paramètres en double, comme,

  a=1&a=2&b=3

Ce n’est pas que je recommande ce type d’utilisation des noms de paramètres.

60
ZZ Coder

Voici quelque chose que j'ai rapidement écrit. Je suis sûr que cela peut être amélioré.

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"
    }
}
40

J'ai trouvé une solution simple avec Java 8 et la solution de polygenelubricants].

parameters.entrySet().stream()
    .map(p -> urlEncodeUTF8(p.getKey()) + "=" + urlEncodeUTF8(p.getValue()))
    .reduce((p1, p2) -> p1 + "&" + p2)
    .orElse("");
27
eclipse

Au printemps, il y a un meilleur moyen ..,

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);
15
Senthil Arumugam SP

Mise à jour juin 2016

Je me sentais obligé d’ajouter une réponse après avoir vu trop de réponses de SOF avec des réponses obsolètes ou inadéquates à un problème très courant - une bonne bibliothèque et quelques exemples concrets d’utilisation pour les opérations parse et format.

Utilisez la bibliothèque org.Apache.httpcomponents.httpclient . La bibliothèque contient cet utilitaire de classe org.Apache.http.client.utils.URLEncodedUtils .

Par exemple, il est facile de télécharger cette dépendance depuis Maven:

 <dependency>
    <groupId>org.Apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5</version>
 </dependency>

Pour mes besoins, je n'avais besoin que de chaînes de requête parse (lues d'une chaîne de requête à des paires nom-valeur) et format (lues d'une paire nom-valeur à une chaîne de requête). Cependant, il existe des équivalents pour faire la même chose avec un URI (voir la ligne commentée ci-dessous).

// Importations requises

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;

// extrait de code

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
    }

Cette bibliothèque a fait exactement ce dont j'avais besoin et a pu remplacer du code personnalisé piraté.

15
arcseldon

Si vous voulez réellement créer un URI complet, essayez RIBuilder à partir de Composants Apache Http (HttpClient 4).

Cela ne répond pas vraiment à la question, mais à celle que j'avais quand j'ai trouvé cette question.

10
Thraidh

Je voulais développer la réponse de @ Eclipse en utilisant le mappage et la réduction Java 8).

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

}

L'opération extra map prend la chaîne réduite et place un ? devant uniquement si la chaîne existe.

7
sbzoom

Une autre manière de le faire 'one class'/no dependency, en traitant un/plusieurs:

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

Vous pouvez utiliser soit simple (plus facile à écrire dans la plupart des cas), soit multiple si nécessaire. Notez que les deux peuvent être combinés en ajoutant une esperluette ... Si vous rencontrez un problème, faites-le moi savoir dans les commentaires.

7
Christophe Roussy

C’est la solution que j’ai implémentée, en utilisant Java 8 et org.Apache.http.client.URLEncodedUtils. Il mappe les entrées de la carte dans une liste de BasicNameValuePair, puis utilise URLEncodedUtils d'Apache pour en faire une chaîne de requête.

List<BasicNameValuePair> nameValuePairs = params.entrySet().stream()
   .map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
   .collect(Collectors.toList());

URLEncodedUtils.format(nameValuePairs, Charset.forName("UTF-8"));
4
Marlon Bernardes

Vous pouvez utiliser un Stream pour cela, mais au lieu d’ajouter moi-même des paramètres de requête, j’utiliserais un Uri.Builder. Par exemple:

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&param2=12"), uri);
1
PPartisan

Pour améliorer un peu la réponse de @ Eclipse: Dans Javaland, une mappe de paramètre de requête est généralement représentée par un Map<String, String[]>, une Map<String, List<String>> ou éventuellement une sorte de MultiValueMap<String, String> qui est en quelque sorte la même chose. Dans tous les cas: un paramètre peut généralement avoir plusieurs valeurs. Une solution Java 8 serait donc quelque chose du genre:

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

Rien ne fait partie de Java). Mais, hé, Java est un langage de programmation, alors .. programmons-le!

map.values().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"))

Cela vous donne "param1 = 12 & param2 = cat". Nous devons maintenant joindre l’URL et ce bit ensemble. Vous penseriez pouvoir faire: URL + "?" + theAbove, Mais si l'URL contient déjà un point d'interrogation, vous devez le joindre avec "&" à la place. Une façon de vérifier consiste à vérifier s'il existe déjà un point d'interrogation dans l'URL.

De plus, je ne sais pas trop ce qu'il y a dans votre carte. S'il s'agit d'éléments bruts, vous devez probablement sauvegarder l'appel sur e.getKey() et e.getValue() avec URLEncoder.encode Ou similaire.

Une autre façon de procéder consiste à adopter une vision plus large. Essayez-vous d'ajouter le contenu d'une carte à une URL ou ... essayez-vous de faire une demande HTTP (S) à partir d'un processus Java) avec les éléments de la carte sous la forme (supplémentaire) Les paramètres HTTP? Dans ce dernier cas, vous pouvez chercher dans une bibliothèque http telle que OkHttp qui possède quelques API de Nice pour faire ce travail, vous pouvez alors vous passer de tout besoin de vous occuper de cette URL .

1
rzwitserloot

Utiliser EntrySet et Streams:

map
  .entrySet()
  .stream()
  .map(e -> e.getKey() + "=" + e.getValue())
  .collect(Collectors.joining("&"));
0
SIVA KUMAR

Personnellement, je choisirais une solution comme celle-ci, elle est incroyablement similaire à celle fournie par @rzwitserloot, à la différence près.

Cette solution est petite, simple et propre, elle nécessite très peu de dépendances, qui font toutes partie du package Java Util.

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));
0
JO3-W3B-D3V