关于java:在Android上解析查询字符串

Parsing query strings on Android

Java EE servletrequest.getparametervalues(HAS)。P></

非url.getquery EE的在线平台,简单的在()返回的字符串。P></

什么是正常的方式properly to the the URL查询字符串中解析Java EE when not在线?P></

咆哮> 它是流行在the answers to尝试和使你自己的解析器。This is甚微的兴趣和激励项目编码,但不能说它是好主意,:(P></

generally below are the code代码片断flawed或破断,BTW。them is an for the阅读器运动的兴趣。黑客的攻击和to the the网站,使用他们。P></

查询字符串是parsing问题出发,但阅读和理解上帝is the the spec不平凡。它是使图书馆更好的让一些平台式编码器和硬盘做工作,做修复,为你!P></

></咆哮P></


安卓:

1
2
3
4
5
6
import android.net.Uri;

[...]

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


在Android上,Apache库提供了一个查询解析器:

http://developer.android.com/reference/org/apache/http/client/utils/urlencodedutils.html和http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/utils/urlencodedutils.html


自从安卓M以来,事情变得更复杂了。android.net.uri.getQueryParameter()的答案有一个bug,它会在jellbean之前破坏空格。apache urlencodedutils.parse()起作用,但在L中被否决,在M中被删除。

所以现在最好的答案是urlquerysanitizer。这从API 1级开始就存在,并且仍然存在。它还让您思考一些棘手的问题,比如如何处理特殊字符或重复值。

最简单的代码是

1
2
3
4
5
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


以下是Balusc的答案,但它编译并返回结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}


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

1
2
3
4
5
6
7
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");

如果您使用的是Spring3.1或更高版本(Yikes,希望支持能再进一步),您可以使用UriComponentsUriComponentsBuilder

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

components.getQueryParams()返回MultiValueMap

这里还有一些文档。


对于servlet或JSP页面,可以使用request.getParameter("paramname")获取queryString键/值对。

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

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


分析查询字符串比看起来要复杂一些,这取决于您希望的宽容程度。

首先,查询字符串是ASCII字节。您一次读取一个字节,然后将其转换为字符。如果角色是?或者&;然后它指示参数名称的开头。如果字符为=则表示参数值的开始。如果字符为%,则表示编码字节的开始。这就是问题的症结所在。

当你读取一个%char时,你必须读取接下来的两个字节,并将它们解释为十六进制数字。这意味着接下来的两个字节将是0-9、a-f或a-f。将这两个十六进制数字粘合在一起以获得字节值。但请记住,字节不是字符。你必须知道用什么编码来编码字符。字符_在UTF-8中的编码与在ISO-8859-1中的编码不同。一般来说,不可能知道给定字符集使用了什么编码。我总是使用UTF-8,因为我的网站被配置为总是使用UTF-8为所有内容提供服务,但实际上你不能确定。一些用户代理会告诉您请求中的字符编码;如果您有完整的HTTP请求,您可以尝试读取它。如果你只有一个单独的网址,祝你好运。

不管怎样,假设您使用的是UTF-8或其他一些多字节字符编码,既然您已经解码了一个已编码的字节,那么在捕获下一个字节之前,必须将它放在一边。您需要将所有编码的字节放在一起,因为您不能一次正确地对一个字节进行URL解码。将所有的字节放在一起,然后一次对它们进行解码,以重建您的角色。

另外,如果你想宽容一点,并且考虑到用户代理会破坏URL的话,这会变得更有趣。例如,一些Webmail客户机对事物进行双重编码。还是加倍?&;=chars(例如:http://yoursite.com/blah??p1==v1&&p2==v2)。如果您想要优雅地处理这个问题,您需要向您的解析器添加更多的逻辑。


我有办法做到这一点:

1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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类:

1
2
3
4
5
6
7
8
9
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"";
}

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

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

tagvalue的值为800


在Android上,我尝试使用@diyism answer,但遇到了@rpetrich提出的空格字符问题,例如:我填写了一个表单,其中username ="us+us"password ="pw pw"导致URL字符串看起来像:

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

但是,@diyism code返回"us+us""pw+pw",即不检测空格字符。如果使用%20重写了URL,则会识别空格字符:

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

这将导致以下修复:

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


这对我有用……我不知道为什么每个人都在地图后面,list>我只需要一个简单的名称-值映射。

为了简单起见,我使用了内置的uri.getquery();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}


在android上,可以使用android.net.uri类的uri.parse静态方法来完成繁重的工作。如果你对uris和intent做了什么,你无论如何都会想使用它。


仅供参考,这是我最后得出的结论(基于urlencodedutils,并返回一张地图)。

特征:

  • 它接受URL的查询字符串部分(您可以使用request.getQueryString())
  • 空查询字符串将产生空的Map
  • 没有值(?测试)将映射到空的List

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}

兼容性助手(值存储在字符串数组中,就像servletrequest.getParameterMap()中一样):

1
2
3
4
5
6
7
8
9
10
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;
}

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

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

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

1
sanitzer.setAllowUnregisteredParamaters(true)

打电话之前:

1
sanitzer.parseUrl(yourUrl)


番石榴的多重地图更适合这个。下面是一个简短的清晰版本:

1
2
3
4
5
6
7
8
9
10
11
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);
        }
    }

ApacheAxis2有一个独立的queryStringParser.java实现。如果您不使用axis2,只需从这里下载源代码和测试用例。-

http://svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/kernel/src/org/apache/axis2/transport/http/util/querystringparser.java

http://svn.apache.org/repos/asf/axis/axis2/java/core/trunk/modules/kernel/test/org/apache/axis2/transport/http/util/querystringparsertest.java


奥利加纳利在这里回答

在android上,包android.net中有uri类。请注意,uri是android.net的一部分,而uri是java.net的一部分。

URI类有许多函数来提取查询键值对。enter image description here

下面的函数以hashmap的形式返回键值对。

在Java中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}

使用apache httpcomponents并将其与一些收集代码连接起来,以便按值访问参数:http://www.joelgerard.com/2012/09/14/parsing-query-strings-in-java-and-accessing-values-by-key/


番石榴:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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
            }
        }
    }

我觉得JRE里没有。您可以在其他包中找到类似的函数,如ApacheHTTPClient。如果你不使用任何其他软件包,你只需要自己写。这并不难。这是我用的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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;
 }
}


回答这里是因为这是一个流行的话题。这是Kotlin中使用推荐的UrlQuerySanitizerAPI的干净解决方案。见官方文件。我添加了一个字符串生成器来连接和显示参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    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
Value: $parameterValue

"
)
    }

    // 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

$paramListString"


    // to check if the url has specific keys
    if (sanitizer.hasParameter("type")) {
        val type = sanitizer.getValue("type")
        println("sanitizer has type param $type")
    }

此方法获取par名称和par值的uri和返回映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  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;
}


你说的是"Java",而不是"Java EE"。您的意思是使用JSP和/或servlet,而不是完整的JavaEE堆栈?如果是这样,那么您应该仍然可以使用request.getParameter()。

如果你的意思是你正在编写Java,但是你不是在写JSP和servlet,或者你只是用Java作为你的参考点,但是你在其他没有内置参数解析的平台上…哇,这听起来不太可能,但如果是这样,原则是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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可用,你可以只使用请求。