在Android上解析查询字符串


271

Java EE具有ServletRequest.getParameterValues()

在非EE平台上,URL.getQuery()仅返回一个字符串。

不在 Java EE上时,正确解析URL中的查询字符串的正常方法是什么?


< rant >

尝试创建自己的解析器的答案很流行。这是一个非常有趣且令人兴奋的微编码项目,但我不能说这是一个好主意 :(

以下代码片段通常有缺陷或损坏,顺便说一句。打破它们对于读者来说是一个有趣的练习。 并向黑客攻击使用它们的网站

解析查询字符串是一个定义明确的问题,但阅读规范和理解细微差别并非易事。最好让一些平台库编码器为您完成工作,并进行修复!

< / rant >


您能张贴一个示例URL,您从中获得getQuery()什么以及您想从输出中获得什么吗?
Thomas Owens

1
您是否要从servlet或JSP页面执行此操作?在回答之前,我需要澄清一下。
ChadNC

1
您还需要解析POST参数吗?
Thilo

2
即使您使用的是J2EE(或者像我一样,使用通过OSGi添加的选定EE软件包的SE),这个问题也很有意义。在我的情况下,查询字符串/ url编码的POST正文由系统的一部分专门处理,例如ServletRequest
汉诺·菲茨

61
为<rant />投票!
Nowaker

Answers:


65

由于Android M,事情变得更加复杂。android.net.URI .getQueryParameter()的答案有一个错误,该错误会中断JellyBean之前的空格。 阿帕奇URLEncodedUtils.parse()工作,但以L弃用,并以M删除

因此,现在最好的答案是UrlQuerySanitizer。自API级别1起就存在,并且仍然存在。它还使您考虑棘手的问题,例如如何处理特殊字符或重复值。

最简单的代码是

UrlQuerySanitizer.ValueSanitizer sanitizer = UrlQuerySanitizer.getAllButNullLegal();
// remember to decide if you want the first or last parameter with the same name
// If you want the first call setPreferFirstRepeatedParameter(true);
sanitizer.parseUrl(url);
String value = sanitizer.getValue("paramName"); // get your value

如果您对默认的解析行为感到满意,则可以执行以下操作:

new UrlQuerySanitizer(url).getValue("paramName")

但是您应该确保了解默认的解析行为是什么,因为它可能不是您想要的。


4
URLQuerySanitizer.getAllButNullLegal()返回UrlQuerySanitizer.ValueSanitizer,而不是UrlQuerySanitizer。
Peter Zhao

31
出于某种原因,以上内容对我不起作用,我不得不对其进行了少许修改-这使其更加容易:UrlQuerySanitizer sanitizer = new UrlQuerySanitizer(YourStringURL); String value = sanitizer.getValue("parameter");
SeBsZ 2016年

3
不行。UrlQuerySanitizer 在sdk-23中只有一种方法sanitize()
Ninja

这会将特殊字符和表情符号解码为_。我不得不使用stackoverflow.com/a/35638979/1155282
Irshu,

是否有与此等效的Spring框架库?
iamjoshua

202

在Android上:

import android.net.Uri;

[...]

Uri uri=Uri.parse(url_string);
uri.getQueryParameter("para1");

20
请注意,这使用的是Uri类而不是URI类(Uri是android.net的一部分,而URI是java.net的一部分)
Marius 2012年

5
另请注意,在“冰淇淋三明治”之前,这无法将值中的+字符解析为空格字符。
rpetrich

@rpetrich实际上,文档说该错误早于Jelly Bean,包括Ice Cream Sandwich。REF
大McLargeHuge

64

9
apache http客户端库中提供了此功能,不仅适用于Android。顺便说一句,到Apache的链接已更改。最新的是:hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/...
克里斯蒂安Vrabie

9
令人讨厌地URLEncodedUtils.parse()返回a List,然后您必须循环遍历以找到特定键的值。如果它Map在BalusC的回答中返回“ like”,那就更好了。
Asaph

1
@Hanno Fietz您是说您相信这些替代方案?我知道他们是越野车。我知道指出我看到的错误只会鼓励人们采用“固定”版本,而不是自己寻找我忽略的错误。
威尔

1
@Will-好吧,我永远不会只相信我从任何网站上获得的复制和粘贴片段,而且没有人应该这样做。但是在这里,这些摘要经过了很好的审查和评论,因此are实际上很有帮助。仅查看有关代码可能存在问题的一些建议,已经对自己进行思考很有帮助。提醒您,我并不是说“自己做得更好”,而是在我自己的代码中拥有能够做出明智决定的良好材料真是太好了。
汉诺·菲茨

8
我想象parse返回一个列表,以便它保持位置顺序并更容易允许重复的条目。
dhaag23 2011年

26

这是BalusC的答案,但它会编译并返回结果:

public static Map<String, List<String>> getUrlParameters(String url)
        throws UnsupportedEncodingException {
    Map<String, List<String>> params = new HashMap<String, List<String>>();
    String[] urlParts = url.split("\\?");
    if (urlParts.length > 1) {
        String query = urlParts[1];
        for (String param : query.split("&")) {
            String pair[] = param.split("=");
            String key = URLDecoder.decode(pair[0], "UTF-8");
            String value = "";
            if (pair.length > 1) {
                value = URLDecoder.decode(pair[1], "UTF-8");
            }
            List<String> values = params.get(key);
            if (values == null) {
                values = new ArrayList<String>();
                params.put(key, values);
            }
            values.add(value);
        }
    }
    return params;
}

1
JVM注意:我已经使用Java集合在Scala中实现了等效形式。这是github的要点:gist.github.com/3504765
Jay Taylor

2
我建议更改String pair[] = param.split("=");String pair[] = param.split("=", 2);仅在第一次出现时才拆分键=值对。我相信允许在值中包含未编码的等号。
丹尼

22

如果您的类路径上有jetty(服务器或客户端)库,则可以使用jetty util类(请参阅javadoc),例如:

import org.eclipse.jetty.util.*;
URL url = new URL("www.example.com/index.php?foo=bar&bla=blub");
MultiMap<String> params = new MultiMap<String>();
UrlEncoded.decodeTo(url.getQuery(), params, "UTF-8");

assert params.getString("foo").equals("bar");
assert params.getString("bla").equals("blub");

13

如果您使用的是Spring 3.1或更高版本(喜欢,希望能够进一步支持),则可以使用UriComponentsand UriComponentsBuilder

UriComponents components = UriComponentsBuilder.fromUri(uri).build();
List<String> myParam = components.getQueryParams().get("myParam");

components.getQueryParams() 返回一个 MultiValueMap<String, String>

这是更多文档


这是我在寻找的东西。我的问题是如何获得尿酸?我一直坚持不能更改太多的代码,也没有使用HttpServlet。而是仅使用批注和Spring(@ Get,@ Produces(mediaType)和@Path(“ / dataAsJSON / datafield / {datafield})))只需要知道如何获取查询字符串,以便我可以解析它,如图所示。此示例
Nelda.techspiress

5

对于servlet或JSP页面,您可以使用request.getParameter(“ paramname”)获取查询字符串键/值对。

String name = request.getParameter("name");

还有其他方法可以做到这一点,但这就是我在创建的所有servlet和jsp页面中都做到的方式。


3
HttpServletRequest是J2EE的一部分,他没有。同样使用getParamter()并不是真正的解析。
Shiny先生和新安宇

3
请花时间阅读我要求澄清他的问题的评论。这个答案是对他对他的评论的回答,他说:“我正在尝试在Android上执行此操作,但是所有平台上的所有答案都是有用的答案,可能会提供指示(也指向可能遇到此问题的其他人)问题),所以不要退缩!” 基于这一评论,我回答了他的问题。如果您没有有用的添加信息,请不要添加任何内容
ChadNC

1
不要太难过。IMO补充说:“这不能回答问题”。
闪亮先生和新安宇

1
Android无关紧要,问题是如何解析包含URL的String并从中获取URL参数。您要在此处移植的是Servlet API的一部分,其中Servlet容器为您解析来自HTTP请求的传入参数。这是无关紧要的,因为问题在于解析包含URL的String,而不是HTTP请求,并且不在Servlet容器内部。
mvmn 2012年

5

在Android上,我尝试使用@diyism的答案,但我遇到了由@rpetrich提出的空格字符的问题,例如:我填写一个表格,username = "us+us"password = "pw pw"导致URL字符串的样子:

http://somewhere?username=us%2Bus&password=pw+pw

但是,@diyism代码返回"us+us""pw+pw",即它没有检测到空格字符。如果使用%20空格字符重写了URL,则会得到以下标识:

http://somewhere?username=us%2Bus&password=pw%20pw

这导致以下修复:

Uri uri = Uri.parse(url_string.replace("+", "%20"));
uri.getQueryParameter("para1");

replace(" ", "%20")这感觉不对。但是,没有的伎俩对我来说:d
马丁斯Briedis

正确的语法应为“某些字符串”。replaceAll(“ [+]”,“%20”);
RRTW

4

解析查询字符串比看起来要复杂一些,具体取决于您想要的宽容程度。

首先,查询字符串是ascii字节。您一次读一次这些字节,然后将它们转换为字符。如果字符是?或&然后表示参数名称的开始。如果字符为=,则表示已开始参数值。如果字符为%,则表示已编码字节的开始。这是棘手的地方。

读入%char时,必须读取接下来的两个字节并将它们解释为十六进制数字。这意味着接下来的两个字节将是0-9,af或AF。将这两个十六进制数字粘合在一起以获得字节值。但是请记住,字节不是character。您必须知道用于编码字符的编码。字符é在UTF-8中的编码方式与在ISO-8859-1中的编码方式不同。通常,不可能知道给定字符集使用了哪种编码。我始终使用UTF-8,因为我的网站配置为始终使用UTF-8来提供所有服务,但是实际上您不确定。一些用户代理会告诉您请求中的字符编码。如果您有完整的HTTP请求,则可以尝试阅读。如果您只是隔离网址,那么祝您好运。

无论如何,假设您使用的是UTF-8或其他某种多字节字符编码,那么既然您已经解码了一个编码字节,则必须将其放在一边,直到捕获下一个字节为止。您需要所有在一起的已编码字节,因为您一次不能正确地对一个字节进行url解码。保留所有在一起的字节,然后立即将它们全部解码以重建字符。

另外,如果您想宽大处理帐户用户代理,那么它会变得更加有趣。例如,某些Webmail客户端对内容进行双重编码。或将?&=字符加倍(例如:) http://yoursite.com/blah??p1==v1&&p2==v2。如果要尝试妥善处理此问题,则需要向解析器添加更多逻辑。


那不能解释如何解析或检索查询字符串参数值
ChadNC

是的,但是有点麻烦。为此,我们已经有了URLDecoder。
BalusC,2009年

2
@ChadNC:第三句话告诉您如何解析:一次读取一个字节并转换为chars。第四句话警告您特殊字符。等等,也许您没有阅读答案?
Shiny先生和新安宇

@BalusC:URLDecoder可以工作,但是如果您尝试宽大接受哪种URL,它会有一些失败模式。
Shiny和New安宇

1
同意@ Mr.ShinyAndNew解析查询参数并不容易。我支持FIQL,这实在是太难了。例如:yoursite.com/blah
p1 == v1 && p2 == v2,p2 == v3; p2 == v4

4

在Android上,其简单代码如下:

UrlQuerySanitizer sanitzer = new UrlQuerySanitizer(url);
String value = sanitzer.getValue("your_get_parameter");

另外,如果您不想注册每个预期的查询键,请使用:

sanitzer.setAllowUnregisteredParamaters(true)

致电之前:

sanitzer.parseUrl(yourUrl)

4

我有实现此目的的方法:

1)

public static String getQueryString(String url, String tag) {
    String[] params = url.split("&");
    Map<String, String> map = new HashMap<String, String>();
    for (String param : params) {
        String name = param.split("=")[0];
        String value = param.split("=")[1];
        map.put(name, value);
    }

    Set<String> keys = map.keySet();
    for (String key : keys) {
        if(key.equals(tag)){
         return map.get(key);
        }
        System.out.println("Name=" + key);
        System.out.println("Value=" + map.get(key));
    }
    return "";
}

2)以及使用Uri类的最简单方法:

public static String getQueryString(String url, String tag) {
    try {
        Uri uri=Uri.parse(url);
        return uri.getQueryParameter(tag);
    }catch(Exception e){
        Log.e(TAG,"getQueryString() " + e.getMessage());
    }
    return "";
}

这是如何使用两种方法之一的示例:

String url = "http://www.jorgesys.com/advertisements/publicidadmobile.htm?position=x46&site=reform&awidth=800&aheight=120";      
String tagValue = getQueryString(url,"awidth");

tagValue的值为 800



3

仅供参考,这是我最后得到的结果(基于URLEncodedUtils,并返回了Map)。

特征:

  • 它接受网址的查询字符串部分(可以使用request.getQueryString()
  • 一个空的查询字符串将产生一个空 Map
  • 没有值的参数(?test)将被映射为空 List<String>

码:

public static Map<String, List<String>> getParameterMapOfLists(String queryString) {
    Map<String, List<String>> mapOfLists = new HashMap<String, List<String>>();
    if (queryString == null || queryString.length() == 0) {
        return mapOfLists;
    }
    List<NameValuePair> list = URLEncodedUtils.parse(URI.create("http://localhost/?" + queryString), "UTF-8");
    for (NameValuePair pair : list) {
        List<String> values = mapOfLists.get(pair.getName());
        if (values == null) {
            values = new ArrayList<String>();
            mapOfLists.put(pair.getName(), values);
        }
        if (pair.getValue() != null) {
            values.add(pair.getValue());
        }
    }

    return mapOfLists;
}

兼容性帮助器(值存储在String数组中,就像ServletRequest.getParameterMap()一样):

public static Map<String, String[]> getParameterMap(String queryString) {
    Map<String, List<String>> mapOfLists = getParameterMapOfLists(queryString);

    Map<String, String[]> mapOfArrays = new HashMap<String, String[]>();
    for (String key : mapOfLists.keySet()) {
        mapOfArrays.put(key, mapOfLists.get(key).toArray(new String[] {}));
    }

    return mapOfArrays;
}

3

这对我有用。我不确定为什么每个人都在Map之后,List>我所需要的只是一个简单的名称值Map。

为了简单起见,我在URI.getQuery()中使用了构建;

public static Map<String, String> getUrlParameters(URI uri)
    throws UnsupportedEncodingException {
    Map<String, String> params = new HashMap<String, String>();
    for (String param : uri.getQuery().split("&")) {
        String pair[] = param.split("=");
        String key = URLDecoder.decode(pair[0], "UTF-8");
        String value = "";
        if (pair.length > 1) {
            value = URLDecoder.decode(pair[1], "UTF-8");
        }
        params.put(new String(key), new String(value));
    }
    return params;
}

1
多选表格怎么样?在合法查询字符串(和POST表单主体)中重复键是完全正常的。还有其他缺陷和角落情况未包括在内;在其他方法的评论中提到了许多方法。我会避免形式指点出来的恐惧,你解决它,而不是使用质量库,按我的咆哮);在问题
威尔

2

番石榴的Multimap更适合于此。这是一个简短的干净版本:

Multimap<String, String> getUrlParameters(String url) {
        try {
            Multimap<String, String> ret = ArrayListMultimap.create();
            for (NameValuePair param : URLEncodedUtils.parse(new URI(url), "UTF-8")) {
                ret.put(param.getName(), param.getValue());
            }
            return ret;
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }


1

最初在这里回答

在Android上,包android.net中有Uri类。请注意,Uri是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;
}

在科特林:

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
    }

0

我认为JRE中没有一个。您可以在其他包(例如Apache HttpClient)中找到类似的功能。如果您不使用任何其他软件包,则只​​需编写自己的软件包。没那么难。这是我用的

public class QueryString {

 private Map<String, List<String>> parameters;

 public QueryString(String qs) {
  parameters = new TreeMap<String, List<String>>();

  // Parse query string
     String pairs[] = qs.split("&");
     for (String pair : pairs) {
            String name;
            String value;
            int pos = pair.indexOf('=');
            // for "n=", the value is "", for "n", the value is null
         if (pos == -1) {
          name = pair;
          value = null;
         } else {
       try {
        name = URLDecoder.decode(pair.substring(0, pos), "UTF-8");
              value = URLDecoder.decode(pair.substring(pos+1, pair.length()), "UTF-8");            
       } catch (UnsupportedEncodingException e) {
        // Not really possible, throw unchecked
           throw new IllegalStateException("No UTF-8");
       }
         }
         List<String> list = parameters.get(name);
         if (list == null) {
          list = new ArrayList<String>();
          parameters.put(name, list);
         }
         list.add(value);
     }
 }

 public String getParameter(String name) {        
  List<String> values = parameters.get(name);
  if (values == null)
   return null;

  if (values.size() == 0)
   return "";

  return values.get(0);
 }

 public String[] getParameterValues(String name) {        
  List<String> values = parameters.get(name);
  if (values == null)
   return null;

  return (String[])values.toArray(new String[values.size()]);
 }

 public Enumeration<String> getParameterNames() {  
  return Collections.enumeration(parameters.keySet()); 
 }

 public Map<String, String[]> getParameterMap() {
  Map<String, String[]> map = new TreeMap<String, String[]>();
  for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
   List<String> list = entry.getValue();
   String[] values;
   if (list == null)
    values = null;
   else
    values = (String[]) list.toArray(new String[list.size()]);
   map.put(entry.getKey(), values);
  }
  return map;
 } 
}

apache类是怎么回事?
威尔


3
请在自己的答案中放入apache commons链接,以便我投票赞成。
itsadok

0

基于BalusC的回答,我编写了一些示例Java代码:

    if (queryString != null)
    {
        final String[] arrParameters = queryString.split("&");
        for (final String tempParameterString : arrParameters)
        {
            final String[] arrTempParameter = tempParameterString.split("=");
            if (arrTempParameter.length >= 2)
            {
                final String parameterKey = arrTempParameter[0];
                final String parameterValue = arrTempParameter[1];
                //do something with the parameters
            }
        }
    }

0
public static Map <String, String> parseQueryString (final URL url)
        throws UnsupportedEncodingException
{
    final Map <String, String> qps = new TreeMap <String, String> ();
    final StringTokenizer pairs = new StringTokenizer (url.getQuery (), "&");
    while (pairs.hasMoreTokens ())
    {
        final String pair = pairs.nextToken ();
        final StringTokenizer parts = new StringTokenizer (pair, "=");
        final String name = URLDecoder.decode (parts.nextToken (), "ISO-8859-1");
        final String value = URLDecoder.decode (parts.nextToken (), "ISO-8859-1");
        qps.put (name, value);
    }
    return qps;
}


0

使用番石榴:

Multimap<String,String> parseQueryString(String queryString, String encoding) {
    LinkedListMultimap<String, String> result = LinkedListMultimap.create();

    for(String entry : Splitter.on("&").omitEmptyStrings().split(queryString)) {
        String pair [] = entry.split("=", 2);
        try {
            result.put(URLDecoder.decode(pair[0], encoding), pair.length == 2 ? URLDecoder.decode(pair[1], encoding) : null);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    return result;
}

0

在这里回答,因为这是一个受欢迎的话题。这是Kotlin中使用推荐UrlQuerySanitizerapi 的干净解决方案。请参阅官方文档。我添加了一个字符串生成器来连接和显示参数。

    var myURL: String? = null
    // if the url is sent from a different activity where you set it to a value
    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")
    }

-2

此方法采用uri并返回参数名称和参数值的映射

  public static Map<String, String> getQueryMap(String uri) {

    String queryParms[] = uri.split("\\?");

    Map<String, String> map = new HashMap<>();// 

    if (queryParms == null || queryParms.length == 0) return map;

    String[] params = queryParms[1].split("&");
    for (String param : params) {
        String name = param.split("=")[0];
        String value = param.split("=")[1];
        map.put(name, value);
    }
    return map;
}

1
根据我上面的说法,这很容易崩溃。不要打扰修复,只需使用专业的实用程序库即可。
2015年

-3

您说的是“ Java”,但不是“ Java EE”。您是说使用的是JSP和/或servlet,而不是完整的Java EE堆栈吗?如果是这种情况,那么您仍然应该可以使用request.getParameter()。

如果您是说您正在编写Java但未编写JSP或servlet,或者您只是使用Java作为参考点,但您使用的是其他没有内置参数解析功能的平台,那么…… ,这听起来像是一个不太可能的问题,但如果是这样,原理将是:

xparm=0
word=""
loop
  get next char
  if no char
    exit loop
  if char=='='
    param_name[xparm]=word
    word=""
  else if char=='&'
    param_value[xparm]=word
    word=""
    xparm=xparm+1
  else if char=='%'
    read next two chars
    word=word+interpret the chars as hex digits to make a byte
  else
    word=word+char

(我可以编写Java代码,但这将毫无意义,因为如果您有Java,则可以只使用request.getParameters。)


在对十六进制数字进行url解码时,请注意字符编码。
Shiny先生和新安宇

它是Android,因此是Java,但不是J2EE。
Andrzej Doyle

我忘了提:您还需要检查“ +”,应将其翻译为空格。查询字符串中的嵌入式空格是非法的。
杰伊,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.