【Java】 URL の クエリ文字列 を組み立てる方法 【いくつか】

【Java】 URL の クエリ文字列 を組み立てる方法 【いくつか】

「やっぱり、プログラム内で URL のクエリを動的にいじるなら、連想配列かで扱いたい!」
「どうせ、そういうメソッドか、クラスあるでしょう?」→調べる→ない
というわけで、クエリ文字列 を連想配列から組み立てる方法をいくつか書いてみます。
公式じゃないライブラリで、そういう機能を持つものなら存在するので、それを使ってもいいんですけど、この機能が使いたいがために、わざわざダウンロードしてくるのもメンドウなので。

1.StringBuilder を使う

まずは地道にやる方法。

// クエリ文字列をくっつける元の URL
String path = "https://www.google.co.jp/search";

// クエリ文字列を連想配列に入れる
Map<String, String> map = new HashMap<String, String>();
map.put("q", "Java");

// クエリ文字列組み立て・URL との連結
// 必ず末尾に余計な文字 ("?" or "&") が残るので、
// String に変換時、substring で削る
StringBuilder builder = new StringBuilder(path);
builder.append("?");
for (Map.Entry<String, String> param: map.entrySet()) {
builder.append(param.getKey());
builder.append("=");
builder.append(param.getValue());
builder.append("&");
}

System.out.println(builder.substring(0, builder.length() - 1));

StringBuilder で地道に連結する場合、最初のみか最後のみ、"&"を取り除かなければいけませんが、連想配列だとそれが判別できないので、連結し終わってから最後の文字を削除します。もし連想配列が空なら、クエッションマークが削除されます。

2.StringJoiner を使う

もうちょっと簡潔に。

// クエリ文字列をくっつける元の URL
String path = "https://www.google.co.jp/search";

// クエリ文字列を連想配列に入れる
Map<String, String> map = new HashMap<String, String>();
map.put("q", "Java");

// まずクエリ文字列を組み立てる
StringJoiner query = new StringJoiner("&");
for (Map.Entry<String, String> param: map.entrySet()) {
query.add(param.getKey() + "=" + param.getValue());
}

// それから URL を組み立てる
StringJoiner url = new StringJoiner("?");
url.add(path);
url.add(query.toString());

System.out.println(url.toString());

StringJoiner を使えば、地道に連結しなくても、区切り文字列を指定して連結できます。
ただ、このプログラムだと、query の状態をチェックせずに add してしまっているので、連想配列が空の場合でもクエッションマークが最後についてしまいます。
1.の場合と違い、最後に余計な文字が付くかどうかは場合により変わってしまうため、query の状態をチェックして処理することになりますが、通信するうえではワザワザそんな処理しなくてもいいっしょ。
ほんとは StringJoiner が CharSequence インターフェースを実装しててくれれば、そのままもうひとつの CharSequence に渡したいところですが、できないのでしょうがなく途中で toString() してます。ホントになんでだろうなぁ。

3.StringJoiner を使う (手抜きver)

これが一番楽。

 // クエリ文字列をくっつける元の URL
String path = "https://www.google.co.jp/search";

// クエリ文字列を連想配列に入れる
Map<String, String> map = new HashMap<String, String>();
map.put("q", "Java");

// クエリ文字列組み立て・URL との連結
StringJoiner url = new StringJoiner("&", path + "?", "");
for (Map.Entry<String, String> param: map.entrySet()) {
url.add(param.getKey() + "=" + param.getValue());
}

System.out.println(url.toString());

連結処理はたった 4行 (for文を 1行で書けば 2行) で書く方法です。
StringJoiner で区切り文字以外に始まりの文字列を指定しています。
ただ、この方法だとどう頑張ってもクエッションマークが付いてしまうので、どうしても、クエリが空の時にクエッションマークを付けたくないなら、2.の時以上にメンドウそうです。(プログラム書くの自体はそんなにメンドウじゃないけど、まるまる連結処理を飛ばして path だけ返す、みたいになるだろうから、キモチわるい)

まとめというか雑談

Java 8 から新たに導入された StringJoiner クラス、大変便利なのですが、なんで CharSequence インターフェース実装してないんですかねぇ。StringBuilder みたくそのまま渡せれば楽なのに~。
自分はまだまだ初心者なので、もしかしたら実装できない・実装しないほうがいい理由があるのかもしれませんね。ご存知の方がいたら教えてください!!
では今日はこのへんで
ノシ
↓ブログランキング投票 (クリック) お願いします~