parsingAndroid上的查询string

Java EE有ServletRequest.getParameterValues() 。

在非EE平台上, URL.getQuery()只是返回一个string。

使用Java EE时,正确parsingURL中的查询string的正常方法是什么?


< rant >

这是受欢迎的答案尝试,使自己的parsing器。 这是非常有趣和令人兴奋的微型编码项目,但我不能说这是一个好主意 🙁

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

parsing查询string是一个明确的问题,但是阅读规范和理解细微差别并不是微不足道的。 让一些平台库编码人员努力工作,为您做好修复,这是好得多!

< / rant >

由于Android M的事情变得更加复杂。 android.net.URI .getQueryParameter()的答案有一个在JellyBean之前打破空格的bug。 Apache 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 

在Android上:

 import android.net.Uri; [...] Uri uri=Uri.parse(url_string); uri.getQueryParameter("para1"); 

这里是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; } 

如果您的类path上有docker(服务器或客户端)库,则可以使用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"); 

如果你使用的是Spring 3.1或更高版本(yikes,希望支持更进一步),你可以使用UriComponentsUriComponentsBuilder

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

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

这是一些更多的文档 。

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

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

还有其他的方法,但是我在所有创build的servlet和jsp页面中都是这样做的。

parsing查询string比看起来更复杂一些,取决于你想要的宽容。

首先,查询string是ASCII字节。 您一次只读取一个字节,并将其转换为字符。 如果angular色是? 或者&然后它表示参数名称的开始。 如果字符是=,那么它表示参数值的开始。 如果字符是%,那么它表示编码字节的开始。 这是棘手的地方。

当读入%char时,必须读取下两个字节并将它们解释为hex数字。 这意味着接下来的两个字节将是0-9,af或AF。 将这两个hex数字粘在一起以获得您的字节值。 但请记住, 字节不是字符 。 你必须知道用什么编码来编码字符。 UTF-8中的字符不会像ISO-8859-1那样编码。 一般来说,不可能知道给定字符集使用了什么编码。 我总是使用UTF-8,因为我的网站被configuration为始终使用UTF-8来处理所有内容,但在实践中,您无法确定。 一些用户代理会告诉你在请求中的字符编码; 如果您有完整的HTTP请求,您可以尝试阅读。 如果你只是孤立的url,祝你好运。

无论如何,假设你正在使用UTF-8或其他多字节字符编码,现在你已经解码了一个编码字节,你必须把它放在一边,直到捕获下一个字节。 您需要将所有编码的字节放在一起,因为一次只能正确解码一个字节。 把所有的字节放在一起,然后一起解码,重build你的angular色。

另外,如果你想要宽大的用户代理,那么就会变得更有趣。 例如,一些networking邮件客户端对事物进行双重编码。 或者双击?&=字符(例如:http: http://yoursite.com/blah??p1==v1&&p2==v2 ??p1==v1&& http://yoursite.com/blah??p1==v1&&p2==v2 )。 如果你想尝试妥善处理这个,你将需要添加更多的逻辑到你的parsing器。

只是作为参考,这是我已经结束了(基于URLEncodedUtils,并返回一个地图)。

特征:

  • 它接受url的查询string部分(你可以使用request.getQueryString()
  • 一个空的查询string将产生一个空的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; } 

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

 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上,我尝试使用@diyism的答案,但是我遇到了@rpetrich引发的空间字符问题,例如:我填写了一个表单,其中username = "us+us"password = "pw pw"导致URLstring喜欢:

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

但是,@diyism代码返回"us+us""pw+pw" ,即它不检测空格字符。 如果URL被重写为%20 ,空格字符被识别:

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

这导致以下修复:

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

我必须采取措施来实现这一点:

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

这对我工作..我不知道为什么每个人之后,一个地图,列表>所有我需要的是一个简单的名称值地图。

为了简单起见,我使用了URI.getQuery()中的构build。

 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静态方法来完成繁重的工作。 如果你对URI和Intents做任何事情,你都会想要使用它。

番石榴的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); } } 

在Android上它的简单代码如下:

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

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

 sanitzer.setAllowUnregisteredParamaters(true) 

打电话之前:

 sanitzer.parseUrl(yourUrl) 

我不认为在JRE有一个。 您可以在Apache HttpClient等其他软件包中find类似的function。 如果你不使用任何其他软件包,你只需要自己写。 这并不难。 这是我用的,

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

根据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 } } } 
 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,并通过一些集合代码连接,以便按值访问params: http : //www.joelgerard.com/2012/09/14/parsing-query-strings-in-java-and-accessing-values-by -键/

使用番石榴:

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

此方法采用名称和面值的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; } 

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

如果你的意思是你正在编写Java,但你不是在写JSP或Servlet,或者你只是使用Java作为你的参考点,但你是在没有内置参数parsing的其他平台上。 ,这听起来像是一个不太可能的问题,但如果是这样的话,原则是:

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