Java 如何将地图转换为 url 查询字符串?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2809877/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:05:41  来源:igfitidea点击:

How to convert map to url query string?

javajakarta-eeutilities

提问by Ula Krukar

Do you know of any utility class/library, that can convert Map into URL-friendly query string?

您知道任何可以将 Map 转换为 URL 友好查询字符串的实用程序类/库吗?

Example:

例子:

I have a map:

我有一张地图:

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

I want to get:

我想得到:

param1=12&param2=cat

final output

最终输出

relativeUrl+param1=12&param2=cat

采纳答案by ZZ Coder

The most robust one I saw off-the-shelf is the URLEncodedUtilsclass from Apache Http Compoments(HttpClient 4.0).

我看到的最强大的现成类是来自Apache Http Compoments(HttpClient 4.0)的URLEncodedUtils类。

The method URLEncodedUtils.format()is what you need.

方法URLEncodedUtils.format()就是你所需要的。

It doesn't use map so you can have duplicate parameter names, like,

它不使用 map 所以你可以有重复的参数名称,比如,

  a=1&a=2&b=3

Not that I recommend this kind of use of parameter names.

并不是我推荐这种使用参数名称。

回答by polygenelubricants

Here's something that I quickly wrote; I'm sure it can be improved upon.

这是我很快写的东西;我相信它可以改进。

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

回答by Thraidh

If you actually want to build a complete URI, try URIBuilderfrom Apache Http Compoments(HttpClient 4).

如果你真的想建立一个完整的URI,尝试URIBuilder的Apache HTTP元件库(HttpClient的4)。

This does not actually answer the question, but it answered the one I had when I found this question.

这实际上并没有回答这个问题,但它回答了我发现这个问题时的问题。

回答by Christophe Roussy

Another 'one class'/no dependency way of doing it, handling single/multiple:

另一种“一类”/无依赖性的方式,处理单个/多个:

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

You may use either simple (easier to write in most cases) or multiple when required. Note that both can be combined by adding an ampersand... If you find any problems let me know in the comments.

您可以在需要时使用 simple(在大多数情况下更容易编写)或多个。请注意,两者都可以通过添加和号来组合...如果您发现任何问题,请在评论中告诉我。

回答by Senthil Arumugam SP

In Spring Util, there is a better way..,

在 Spring Util 中,有一个更好的方法..,

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

回答by eclipse

I found a smooth solution using java 8 and polygenelubricants' solution.

我使用 java 8 和 polygenelubricants 的解决方案找到了一个平滑的解决方案。

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

回答by Marlon Bernardes

This is the solution I implemented, using Java 8 and org.apache.http.client.URLEncodedUtils. It maps the entries of the map into a list of BasicNameValuePairand then uses Apache's URLEncodedUtilsto turn that into a query string.

这是我使用 Java 8 和org.apache.http.client.URLEncodedUtils. 它将地图条目映射到一个列表中BasicNameValuePair,然后使用 ApacheURLEncodedUtils将其转换为查询字符串。

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

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

回答by Auke

To improve a little bit upon @eclipse's answer: In Javaland a request parameter map is usually represented as a Map<String, String[]>, a Map<String, List<String>>or possibly some kind of MultiValueMap<String, String>which is sort of the same thing. In any case: a parameter can usually have multiple values. A Java 8 solution would therefore be something along these lines:

稍微改进@eclipse 的回答:在 Javaland 中,请求参数映射通常表示为 a Map<String, String[]>、 aMap<String, List<String>>或可能MultiValueMap<String, String>是某种相同的东西。在任何情况下:一个参数通常可以有多个值。因此,Java 8 解决方案应该是这样的:

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

回答by arcseldon

Update June 2016

2016 年 6 月更新

Felt compelled to add an answer having seen far too many SOF answers with dated or inadequate answers to very common problem - a good library and some solid example usage for both parseand formatoperations.

在看到太多 SOF 答案时,感到不得不添加一个答案,这些答案对非常常见的问题有过时或不充分的答案 - 一个很好的库和一些适用于parseformat操作的可靠示例用法。

Use org.apache.httpcomponents.httpclientlibrary. The library contains this org.apache.http.client.utils.URLEncodedUtilsclass utility.

使用org.apache.httpcomponents.httpclient库。该库包含此org.apache.http.client.utils.URLEncodedUtils类实用程序。

For example, it is easy to download this dependency from Maven:

例如,很容易从 Maven 下载这个依赖项:

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

For my purposes I only needed to parse(read from query string to name-value pairs) and format(read from name-value pairs to query string) query strings. However, there are equivalents for doing the same with a URI (see commented out line below).

出于我的目的,我只需要parse(从查询字符串读取到名称-值对)和format(从名称-值对读取到查询字符串)查询字符串。但是,也有对 URI 执行相同操作的等效项(请参阅下面注释掉的行)。

// Required imports

// 必需的导入

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 snippet

// 代码片段

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
    }

This library did exactly what I needed and was able to replace some hacked custom code.

这个库完全满足了我的需求,并且能够替换一些被黑的自定义代码。

回答by sbzoom

I wanted to build on @eclipse's answer using java 8 mapping and reducing.

我想使用 java 8 映射和减少以@eclipse 的答案为基础。

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

The extra mapoperation takes the reduced string and puts a ?in front only if the string exists.

额外的map操作采用减少的字符串并?仅在字符串存在时将 a放在前面。