Parse String to get grouped parameters - java

My String looks like this
http://localhost:8080/HospitalServer/files/file?id=34&firstname=alex&lastname=ozouf&age=33&firstname=kevin&lastname=gerfild&age=27
I use this code to parse the parameters
final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
final String[] pairs = query.split("&");
for (String pair : pairs) {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
if (!query_pairs.containsKey(key)) {
query_pairs.put(key, new LinkedList<String>());
}
final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null;
query_pairs.get(key).add(value);
}
System.out.println(query_pairs);
The result is
{id=[34], firstname=[alex, kevin], lastname=[ozouf, gerfild], age=[33, 27]}
The result is not too bad but I want to group the parameters by person.
{id=[34], 1=[alex,ozouf,33 ], 2=[kevin, gerfild,27]}
I can create it from the previous result but I have the feeling that the job is done twice. What do you think I shall do ?

Here's how you can do it without using any library:
import java.util.Map;
import java.util.HashMap;
public class MyUrlParser {
private static final String SEPARATOR = ",";
public static void main(String[] args) {
final String URL = "http://localhost:8080/HospitalServer/files/file?id=34&firstname=alex&lastname=ozouf&age=33&firstname=kevin&lastname=gerfild&age=27";
MyUrlParser mup = new MyUrlParser();
try {
Map<String, String> parsed = mup.parse(URL);
System.out.println(parsed);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public Map<String, String> parse(String url) throws Exception {
Map<String, String> retMap = new HashMap<>();
int queryStringPos = url.indexOf("?");
if (-1 == queryStringPos) {
throw new Exception("Invalid URL");
}
String queryString = url.substring(queryStringPos + 1);
String[] parameters = queryString.split("&");
if (parameters.length > 0) {
retMap.put("id", parameters[0]);
int personCounter = 0;
for (int minSize = 4; minSize <= parameters.length; minSize += 3) {
StringBuilder person = new StringBuilder();
person.append(parameters[minSize-3]);
person.append(SEPARATOR);
person.append(parameters[minSize-2]);
person.append(SEPARATOR);
person.append(parameters[minSize-1]);
personCounter++;
retMap.put("person" + personCounter, person.toString());
}
}
return retMap;
}
}

Related

How do i change method from using HashMap to a normal String method

Someone, please assist to change the method 'getBabyNameFrequencies' in class 'Result' from using HarshMap to a normal String method as is in the main/feeder class 'Solution'
/*
* The function is expected to return a STRING.
* The function accepts the following parameters:
* 1. STRING names
* 2. STRING synonyms
*/
class Solution {
private Map<String, Integer> mp = new HashMap<>();
private Map<String, String> p = new HashMap<>();
public String[] getBabyNameFrequencies(String[] names, String[] synonyms) {
for (String e : names) {
int idx = e.indexOf("(");
String name = e.substring(0, idx);
int w = Integer.parseInt(e.substring(idx + 1, e.length() - 1));
mp.put(name, w);
p.put(name, name);
}
for (String e : synonyms) {
int idx = e.indexOf(",");
String name1 = e.substring(1, idx);
String name2 = e.substring(idx + 1, e.length() - 1);
if (!mp.containsKey(name1)) {
mp.put(name1, 0);
}
if (!mp.containsKey(name2)) {
mp.put(name2, 0);
}
p.put(name1, name1);
p.put(name2, name2);
}
for (String e : synonyms) {
int idx = e.indexOf(",");
String name1 = e.substring(1, idx);
String name2 = e.substring(idx + 1, e.length() - 1);
union(name1, name2);
}
List<String> t = new ArrayList<>();
for (Map.Entry<String, Integer> e : mp.entrySet()) {
String name = e.getKey();
if (Objects.equals(name, find(name))) {
t.add(name + "(" + e.getValue() + ")");
}
}
String[] res = new String[t.size()];
for (int i = 0; i < res.length; ++i) {
res[i] = t.get(i);
}
return res;
}
private String find(String x) {
if (!Objects.equals(p.get(x), x)) {
p.put(x, find(p.get(x)));
}
return p.get(x);
}
private void union(String a, String b) {
String pa = find(a), pb = find(b);
if (Objects.equals(pa, pb)) {
return;
}
if (pa.compareTo(pb) > 0) {
mp.put(pb, mp.getOrDefault(pb, 0) + mp.getOrDefault(pa, 0));
p.put(pa, pb);
} else {
mp.put(pa, mp.getOrDefault(pa, 0) + mp.getOrDefault(pb, 0));
p.put(pb, pa);
}
}
}
The Solution Class/ Feeder Class with the Main method.
public class Solution {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
String names = bufferedReader.readLine();
String synonyms = bufferedReader.readLine();
String result = Result.getBabyNameFrequencies(names, synonyms);
bufferedWriter.write(result);
bufferedWriter.newLine();
bufferedReader.close();
bufferedWriter.close();
}
}

how to connect and insert map keys and values into derby database?

I have a java program and it produces the output as follows :
termname :docname : termcount
Forexample termname is hello and docname is :2 and termcount is :4
hello:doc1:4
.....
......
I stored all the values in a map. here is the following program
public class tuple {
public static void main(String[]args) throws FileNotFoundException, UnsupportedEncodingException, SQLException, ClassNotFoundException, Exception{
File file2 = new File("D:\\logs\\tuple.txt");
PrintWriter tupled = new PrintWriter(file2, "UTF-8");
List<Map<String, Integer>> list = new ArrayList<>();
Map<String, Integer>map= new HashMap<>();;
String word;
//Iterate over documents
for (int i = 1; i <= 2; i++) {
//map = new HashMap<>();
Scanner tdsc = new Scanner(new File("D:\\logs\\AfterStem" + i + ".txt"));
//Iterate over words
while (tdsc.hasNext()) {
word = tdsc.next();
final Integer freq = map.get(word);
if (freq == null) {
map.put(word, 1);
} else {
map.put(word, map.get(word) + 1);
}
}
list.add(map);
}
// tupled.println(list);
//tupled.close();
//Print result
int documentNumber = 0;
for (Map<String, Integer> document : list) {
for (Map.Entry<String, Integer> entry : document.entrySet()) {
documentNumber++;
//System.out.println(entry.getKey() + ":doc"+documentNumber+":" + entry.getValue());
tupled.print(entry.getKey());
tupled.print(":doc:");
tupled.print(Integer.toString(documentNumber));
tupled.print(",");
tupled.println(entry.getValue());
}
//documentNumber++;
}
tupled.close();
Now I want to store this values into derby database of neatbeans.
How I would be able to do that ?

Writing a java algorithm equivalent to the sql select query which uses inner join and group by clause

Need to write the java code (algorithm) equivalent to the below sql query -
select u.browsers, a.app_name, count(*)
as no_of_apps from users u inner join apps a
where u.user_id = a.user_id group by u.browsers, a.app_name order by no_of_apps limit 0,2.
There are two different .tsv files (user.tsv and app.tsv) and the format of data they contains is -
+1.users.tsv
user_id browsers
1 Mozilla
2 Mozilla
3 Chrome
+2. apps.tsv
app_id user_id app_name
1 1 HelloWorld
2 3 Sonar
3 1 Sonar
4 2 HelloWorld
5 1 HelloWorld
Here user_id is acting like a Foreign key in apps.tsv file. What I really need is the algorithm in java to get data from the two files something like this -
browsers app_name no_of_apps
Mozilla HelloWorld 3
Mozilla Sonar 1
Chrome Sonar 1
Code I wrote for this is -
import java.util.*;
import java.util.Map.*;
import java.util.HashMap;
import java.io.*;
import java.nio.file.*;
public class Task1 {
public static void main(String... args) {
//cloumn places
int state_col = 2;
int user_col = 0;
int jobid_col = 2;
List<String> userLines = null;
List<String> appLines = null;
String[][] userResultArray = null;
String[][] appResultArray = null;
Map<String, String> userMap = new HashMap<String,String>();
Map<String, String> appMap = new HashMap<String, String>();
Map<String, String> userJobMap = new HashMap<String,String>();
List<Integer> app = new ArrayList<Integer>();
try {
userLines = Files.readAllLines(Paths.get("/home/indg/java-spring/spring/idea_workspace/Task1/tabFiles/users.tsv"));
appLines = Files.readAllLines(Paths.get("/home/indg/java-spring/spring/idea_workspace/Task1/tabFiles/apps.tsv"));
} catch(Exception ex) {
ex.printStackTrace();
}
if(userLines != null) {
userResultArray = new String[userLines.size()][];
for(int i =0; i<userLines.size()-1; i++){
userResultArray[i] = userLines.get(i).split("\t"); //tab-separated
if(userResultArray[i][state_col] != "" && userResultArray[i][state_col] != null) {
userMap.put(userResultArray[i][user_col],userResultArray[i][state_col]);
}
}
} else {
System.out.println("Error in reading line");
}
if(appLines != null) {
appResultArray = new String[appLines.size()][];
for(int i =0; i<appLines.size()-1; i++){
appResultArray[i] = appLines.get(i).split("\t"); //tab-separated
if(appResultArray[i][jobid_col] != "" && appResultArray[i][jobid_col] != null) {
appMap.put(appResultArray[i][user_col], appResultArray[i][jobid_col]);
}
}
} else {
System.out.println("Error in reading line");
}
for(String userKey: userMap.keySet()) {
//System.out.println(userEntry.getKey() + "**");
String jobId = appMap.get(userKey);
if(jobId != null && userMap.get(userKey) != null && userMap.get(userKey) != "" ) {
userJobMap.put(userMap.get(userKey), jobId);
}
}
System.out.println(userJobMap + " --");
}
}
I want to apply order by and limit in the query too for example - order by no_of_apps limit 0,2 how to do that in the code
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class Task1 {
public static void main(String... args) {
List<String> userLines = Arrays.asList(new String[] { "1\tMozilla", "2\tMozilla", "3\tChrome" });
List<String> appLines = Arrays
.asList(new String[] { "1\t1\tHelloWorld", "2\t3\tSonar", "3\t1\tSonar", "4\t2\tHelloWorld", "5\t1\tHelloWorld" });
Map<String, String> userMap = new HashMap<String, String>();
Map<String, Map<String, Integer>> resultMap = new HashMap<String, Map<String, Integer>>();
if (userLines != null) {
for (int i = 0; i < userLines.size(); i++) {
String[] userResultArray = userLines.get(i).split("\t"); // tab-separated
userMap.put(userResultArray[0], userResultArray[1]);
}
} else {
System.out.println("Error in reading line");
}
if (appLines != null) {
for (int i = 0; i < appLines.size(); i++) {
String[] appResultArray = appLines.get(i).split("\t"); // tab-separated
if (userMap.containsKey(appResultArray[1])) {
String userName = userMap.get(appResultArray[1]);
if (resultMap.containsKey(userName)) {
Map<String, Integer> map = resultMap.get(userName);
Integer n = map.get(appResultArray[2]) == null ? 0 : map.get(appResultArray[2]);
map.put(appResultArray[2], ++n);
} else {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put(appResultArray[2], 1);
resultMap.put(userName, map);
}
}
}
} else {
System.out.println("Error in reading line");
}
// sort
Map<Integer, List<String>> sortedMap = new TreeMap<Integer, List<String>>();
for (String userName : resultMap.keySet()) {
for (String app : resultMap.get(userName).keySet()) {
Integer n = resultMap.get(userName).get(app);
List<String> list = sortedMap.get(n) == null ? new ArrayList<String>() : sortedMap.get(n);
list.add(userName + "\t" + app);
sortedMap.put(n, list);
}
}
int limit = 0;
outer:
for (Integer n : sortedMap.keySet()) {
for (String s : sortedMap.get(n)) {
System.out.print(s);
System.out.println("\t" + n);
if (++limit == 2) break outer;
}
}
}
}

How to extract parameters from a given url

In Java I have:
String params = "depCity=PAR&roomType=D&depCity=NYC";
I want to get values of depCity parameters (PAR,NYC).
So I created regex:
String regex = "depCity=([^&]+)";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(params);
m.find() is returning false. m.groups() is returning IllegalArgumentException.
What am I doing wrong?
It doesn't have to be regex. Since I think there's no standard method to handle this thing, I'm using something that I copied from somewhere (and perhaps modified a bit):
public static Map<String, List<String>> getQueryParams(String url) {
try {
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;
} catch (UnsupportedEncodingException ex) {
throw new AssertionError(ex);
}
}
So, when you call it, you will get all parameters and their values. The method handles multi-valued params, hence the List<String> rather than String, and in your case you'll need to get the first list element.
Not sure how you used find and group, but this works fine:
String params = "depCity=PAR&roomType=D&depCity=NYC";
try {
Pattern p = Pattern.compile("depCity=([^&]+)");
Matcher m = p.matcher(params);
while (m.find()) {
System.out.println(m.group());
}
} catch (PatternSyntaxException ex) {
// error handling
}
However, If you only want the values, not the key depCity= then you can either use m.group(1) or use a regex with lookarounds:
Pattern p = Pattern.compile("(?<=depCity=).*?(?=&|$)");
It works in the same Java code as above. It tries to find a start position right after depCity=. Then matches anything but as little as possible until it reaches a point facing & or end of input.
I have three solutions, the third one is an improved version of Bozho's.
First, if you don't want to write stuff yourself and simply use a lib, then use Apache's httpcomponents lib's URIBuilder class: http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/utils/URIBuilder.html
new URIBuilder("http://...").getQueryParams()...
Second:
// overwrites duplicates
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
public static Map<String, String> readParamsIntoMap(String url, String charset) throws URISyntaxException {
Map<String, String> params = new HashMap<>();
List<NameValuePair> result = URLEncodedUtils.parse(new URI(url), charset);
for (NameValuePair nvp : result) {
params.put(nvp.getName(), nvp.getValue());
}
return params;
}
Third:
public static Map<String, List<String>> getQueryParams(String url) throws UnsupportedEncodingException {
Map<String, List<String>> params = new HashMap<String, List<String>>();
String[] urlParts = url.split("\\?");
if (urlParts.length < 2) {
return params;
}
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");
}
// skip ?& and &&
if ("".equals(key) && pair.length == 1) {
continue;
}
List<String> values = params.get(key);
if (values == null) {
values = new ArrayList<String>();
params.put(key, values);
}
values.add(value);
}
return params;
}
If you are developing an Android application, try this:
String yourParam = null;
Uri uri = Uri.parse(url);
try {
yourParam = URLDecoder.decode(uri.getQueryParameter(PARAM_NAME), "UTF-8");
} catch (UnsupportedEncodingException exception) {
exception.printStackTrace();
}
If spring-web is present on classpath, UriComponentsBuilder can be used.
MultiValueMap<String, String> queryParams =
UriComponentsBuilder.fromUriString(url).build().getQueryParams();
Simple Solution create the map out of all param name and values and use it :).
import org.apache.commons.lang3.StringUtils;
public String splitURL(String url, String parameter){
HashMap<String, String> urlMap=new HashMap<String, String>();
String queryString=StringUtils.substringAfter(url,"?");
for(String param : queryString.split("&")){
urlMap.put(StringUtils.substringBefore(param, "="),StringUtils.substringAfter(param, "="));
}
return urlMap.get(parameter);
}
same but with jsonobject:
public static JSONObject getQueryParams2(String url) {
JSONObject json = new JSONObject();
try {
String[] urlParts = url.split("\\?");
JSONArray array = new JSONArray();
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");
if(json.has(key)) {
array = json.getJSONArray(key);
array.put(value);
json.put(key, array);
array = new JSONArray();
} else {
array.put(value);
json.put(key, array);
array = new JSONArray();
}
}
}
}
return json;
} catch (Exception ex) {
throw new AssertionError(ex);
}
}

Is there any Java equivalent of PHP's http_build_query function?

I have a Map with my data and want to build a query string with it, just like I would with http_build_query on PHP. I'm not sure if this code is the best implementation of it or if I'm forgetting something?
public String toQueryString(Map<?, ?> data) throws UnsupportedEncodingException {
StringBuffer queryString = new StringBuffer();
for (Entry<?, ?> pair : data.entrySet()) {
queryString.append ( URLEncoder.encode ( (String) pair.getKey (), "UTF-8" ) + "=" );
queryString.append ( URLEncoder.encode ( (String) pair.getValue (), "UTF-8" ) + "&" );
}
if (queryString.length () > 0) {
queryString.deleteCharAt ( queryString.length () - 1 );
}
return queryString.toString ();
}
look at the QueryStringBuilder class and its test class :
private String httpBuildQuery(Map<String, String> data)
throws UnsupportedEncodingException {
QueryStringBuilder builder = new QueryStringBuilder();
for (Entry<String, String> pair : data.entrySet()) {
builder.addQueryParameter(pair.getKey(), pair.getValue());
}
return builder.encode("UTF-8");
}
The true power of PHP http_build_query function is its ability to get associative array and translate it into URL string. The following code do similar thing and it allows url params to be constructed as multi level Map that include nested Map and Collections.With some more work this a support for Array can also be added.
Test methods are shown below.
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
/**
* Class: URLBuilder
* User: Gilad Tiram
* Date: 6/12/13
* Time: 4:02 PM
* <p/>
* <p/>
* Utility that helps to build URL String
*/
public class URLBuilder {
/**
* Build URL string from Map of params. Nested Map and Collection is also supported
*
* #param params Map of params for constructing the URL Query String
* #param encoding encoding type. If not set the "UTF-8" is selected by default
* #return String of type key=value&...key=value
* #throws java.io.UnsupportedEncodingException
* if encoding isnot supported
*/
public static String httpBuildQuery(Map<String, Object> params, String encoding) {
if (isEmpty(encoding)) {
encoding = "UTF-8";
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : params.entrySet()) {
if (sb.length() > 0) {
sb.append('&');
}
String name = entry.getKey();
Object value = entry.getValue();
if (value instanceof Map) {
List<String> baseParam = new ArrayList<String>();
baseParam.add(name);
String str = buildUrlFromMap(baseParam, (Map) value, encoding);
sb.append(str);
} else if (value instanceof Collection) {
List<String> baseParam = new ArrayList<String>();
baseParam.add(name);
String str = buildUrlFromCollection(baseParam, (Collection) value, encoding);
sb.append(str);
} else {
sb.append(encodeParam(name));
sb.append("=");
sb.append(encodeParam(value));
}
}
return sb.toString();
}
private static String buildUrlFromMap(List<String> baseParam, Map<Object, Object> map, String encoding) {
StringBuilder sb = new StringBuilder();
String token;
//Build string of first level - related with params of provided Map
for (Map.Entry<Object, Object> entry : map.entrySet()) {
if (sb.length() > 0) {
sb.append('&');
}
String name = String.valueOf(entry.getKey());
Object value = entry.getValue();
if (value instanceof Map) {
List<String> baseParam2 = new ArrayList<String>(baseParam);
baseParam2.add(name);
String str = buildUrlFromMap(baseParam2, (Map) value, encoding);
sb.append(str);
} else if (value instanceof List) {
List<String> baseParam2 = new ArrayList<String>(baseParam);
baseParam2.add(name);
String str = buildUrlFromCollection(baseParam2, (List) value, encoding);
sb.append(str);
} else {
token = getBaseParamString(baseParam) + "[" + name + "]=" + encodeParam(value);
sb.append(token);
}
}
return sb.toString();
}
private static String buildUrlFromCollection(List<String> baseParam, Collection coll, String encoding) {
StringBuilder sb = new StringBuilder();
String token;
if (!(coll instanceof List)) {
coll = new ArrayList(coll);
}
List arrColl = (List) coll;
//Build string of first level - related with params of provided Map
for (int i = 0; i < arrColl.size(); i++) {
if (sb.length() > 0) {
sb.append('&');
}
Object value = (Object) arrColl.get(i);
if (value instanceof Map) {
List<String> baseParam2 = new ArrayList<String>(baseParam);
baseParam2.add(String.valueOf(i));
String str = buildUrlFromMap(baseParam2, (Map) value, encoding);
sb.append(str);
} else if (value instanceof List) {
List<String> baseParam2 = new ArrayList<String>(baseParam);
baseParam2.add(String.valueOf(i));
String str = buildUrlFromCollection(baseParam2, (List) value, encoding);
sb.append(str);
} else {
token = getBaseParamString(baseParam) + "[" + i + "]=" + encodeParam(value);
sb.append(token);
}
}
return sb.toString();
}
private static String getBaseParamString(List<String> baseParam) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < baseParam.size(); i++) {
String s = baseParam.get(i);
if (i == 0) {
sb.append(s);
} else {
sb.append("[" + s + "]");
}
}
return sb.toString();
}
/**
* Check if String is either empty or null
*
* #param str string to check
* #return true if string is empty. Else return false
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
private static String encodeParam(Object param) {
try {
return URLEncoder.encode(String.valueOf(param), "UTF-8");
} catch (UnsupportedEncodingException e) {
return URLEncoder.encode(String.valueOf(param));
}
}
/* ========================================================================= */
/* Test functions */
/* ========================================================================= */
public static void main(String[] args) {
//basicTest();
//testWithMap();
//testWithList();
//testWithNestedMap();
//testWithNestedList();
testCompound();
}
private static void basicTest() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put("a", "1");
params.put("b", "2");
params.put("c", "3");
System.out.println(httpBuildQuery(params, "UTF-8"));
}
private static void testWithMap() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put("a", "1");
params.put("b", "2");
Map<String, Object> cParams = new LinkedHashMap<String, Object>();
cParams.put("c1", "c1val");
cParams.put("c2", "c2val");
params.put("c", cParams);
System.out.println(httpBuildQuery(params, "UTF-8"));
}
private static void testWithNestedMap() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put("a", "1");
params.put("b", "2");
Map<String, Object> cParamsLevel1 = new LinkedHashMap<String, Object>();
cParamsLevel1.put("cL1-1", "cLevel1-1val");
cParamsLevel1.put("cL1-2", "cLevel1-2val");
Map<String, Object> cParamsLevel2 = new LinkedHashMap<String, Object>();
cParamsLevel2.put("cL2-1", "cLevel2-1val");
cParamsLevel2.put("cL2-2", "cLevel2-2val");
cParamsLevel1.put("cL1-3", cParamsLevel2);
params.put("c", cParamsLevel1);
System.out.println(httpBuildQuery(params, "UTF-8"));
}
private static void testWithList() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put("a", "1");
params.put("b", "2");
List<Object> cParams = new ArrayList<Object>();
cParams.add("c1val");
cParams.add("c2val");
params.put("c", cParams);
System.out.println(httpBuildQuery(params, "UTF-8"));
}
private static void testWithNestedList() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put("a", "1");
params.put("b", "2");
List<Object> cParamsLevel1 = new ArrayList<Object>();
cParamsLevel1.add("cL1-val1");
cParamsLevel1.add("cL12-val2");
List<Object> cParamsLevel2 = new ArrayList<Object>();
cParamsLevel2.add("cL2-val1");
cParamsLevel2.add("cL2-val2");
cParamsLevel1.add(cParamsLevel2);
params.put("c", cParamsLevel1);
System.out.println(httpBuildQuery(params, "UTF-8"));
}
private static void testCompound() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
//flat
params.put("a", "1");
params.put("b", "2");
//Map level 1
Map<String, Object> cParamsLevel1 = new LinkedHashMap<String, Object>();
cParamsLevel1.put("cL1-1", "cLevel1-1val");
cParamsLevel1.put("cL1-2", "cLevel1-2val");
//Map level 2
Map<String, Object> cParamsLevel2 = new LinkedHashMap<String, Object>();
cParamsLevel2.put("cL2-1", "cLevel2-1val");
cParamsLevel2.put("cL2-2", "cLevel2-2val");
cParamsLevel1.put("cL1-3", cParamsLevel2);
params.put("c", cParamsLevel1);
//List level 1
List<Object> dParamsLevel1 = new ArrayList<Object>();
dParamsLevel1.add("dL1-val1");
dParamsLevel1.add("dL12-val2");
//List level 2
List<Object> dParamsLevel2 = new ArrayList<Object>();
dParamsLevel2.add("dL2-val1");
dParamsLevel2.add("dL2-val2");
dParamsLevel1.add(dParamsLevel2);
params.put("d", dParamsLevel1);
System.out.println(httpBuildQuery(params, "UTF-8"));
}
}
For easy test of your results append the string resulted by tests as query String of real url that point to this PHP. Example
http://localhost/test.php?a=1&b=2&c[cL1-1]=cLevel1-1val&c[cL1-2]=cLevel1-2val&c[cL1-3][cL2-1]=cLevel2-1val&c[cL1-3][cL2-2]=cLevel2-2val&d[0]=dL1-val1&d[1]=dL12-val2&d[2][0]=dL2-val1&d[2][1]=dL2-val2
<?php
var_dump($_REQUEST);
?>
You'd probably want to check the "Accept" request header for the encodings supported by the client before forcing UTF-8 (even though it's probably the best choice).
This should be the easiest (and most reliable) solution:
protected static String httpBuildQuery(List<? extends NameValuePair> parameters, String encoding) {
return URLEncodedUtils.format(parameters, encoding).replace("*", "%2A");
}
Example usage:
List<NameValuePair> params = new ArrayList<NameValuePair>;
params.add(new BasicNameValuePair("key", "value"));
String queryString = httpBuildQuery(myParamList, "UTF-8");
Java not encoding the asterisk (+) while PHP does encode it %2A should be the only difference.
Looks OK, with these caveats:
make the parameter a Map<String, String> rather than casting key and value to String.
the hardcoded encoding looks suspicious. UTF-8 is not a given, it has to match the encoding defined in the header of the HTTP request. So the code should ensure that they do - at least define it as a constant somewhere and refer to that both here and wherever the request encoding is set.
Edit: It seems that I was wrong about the encoding; HTTP GET parameters are not subject to an encoding header, and did not traditionally have a well-defined encoding at all. RFC 3988 does seem to mandate UTF-8, but that sounds rather brittle to me, so unless you have tight control over the server and can ensure that it does indeed use UTF-8 as well, I'd use POST requests for any data that's not in the 7bit ASCII range.

Categories