tags:

views:

95

answers:

4

In Python I would do the following:

>>> q = urllib.urlencode({"q": "clojure url"})
>>> q
'q=clojure+url'

>>> url = "http://stackoverflow.com/search?" + q
>>> url
'http://stackoverflow.com/search?q=clojure+url'

How do I do all the encoding that's done for me above in Clojure? In other words, how do I do something akin to the following:

=> (build-url "http://stackoverflow.com/search" {"q" "clojure url"})
"http://stackoverflow.com/search?q=clojure+url"
A: 

A simple solution using str(ings):

(def q (str "clojure+url")) (def url (str "http://stackoverflow.com/search?" q))

bruce
+5  A: 

Here's one way:

user=> (import [java.net URLEncoder])                                                      
java.net.URLEncoder
user=> (str "http://stackoverflow.com/search?q=" (URLEncoder/encode "clojure url" "UTF-8"))
"http://stackoverflow.com/search?q=clojure+url"

I know this is not exactly the same as your Python snippet though. Please see the following post from the Clojure mailing list for a more complete answer:

http://www.mail-archive.com/[email protected]/msg29338.html

The code from there will allow you to do this:

user=> (encode-params {"q" "clojure url"})
"q=clojure+url"
Michael Kohl
Noticing the edit, I guess I shouldn't have bothered writing all that... Oh well. +1 for the first complete answer. I still like my `Named`-handling version, so I'm leaving that here. :-)
Michał Marczyk
+3  A: 

There is a url-encode function in Ring's ring.util.codec namespace:

(ring.util.codec/url-encode "clojure url")
; => "clojure+url"

I'm not sure if there's a prepackaged function to turn a map into a query string, but perhaps this could do the job:

(use '[ring.util.codec :only [url-encode]])

(defn make-query-string [m]
  (->> (for [[k v] m]
         (str (url-encode k) "=" (url-encode v)))
       (interpose "&")
       (apply str)))

An example:

user> (make-query-string {"q" "clojure url" "foo" "bar"})
"q=clojure+url&foo=bar"

All that remains is concatenating the result onto the end of a URL:

(defn build-url [url-base query-map]
  (str url-base "?" (make-query-string query-map)))

Seems to work:

user> (build-url "http://stackoverflow.com/search" {"q" "clojure url"})
"http://stackoverflow.com/search?q=clojure+url"

Update:

Perhaps a modified version might make for a more Clojure-friendly experience. Also handles encoding via a Ring-style optional argument with utf-8 as default.

(defn make-query-string [m & [encoding]]
  (let [s #(if (instance? clojure.lang.Named %) (name %) %)
        enc (or encoding "UTF-8")]
    (->> (for [[k v] m]
           (str (url-encode (s k) enc)
                "="
                (url-encode (str v) enc)))
         (interpose "&")
         (apply str))))

(defn build-url [url-base query-map & [encoding]]
  (str url-base "?" (make-query-string query-map encoding)))

So now we can do

user> (build-url "http://example.com/q" {:foo 1})
"http://example.com/q?foo=1"
Michał Marczyk
Ah, almost forgot: `ring.util.codec` also takes an optional encoding argument, defaulting to `UTF-8`.
Michał Marczyk
A: 

clj-apache-http is pretty useful. With it you can do the following:

user=> (require ['com.twinql.clojure.http :as 'http])
nil
user=> (def q (http/encode-query {"q" "clojure url"}))
#'user/q
user=> (def url (str "http://stackoverflow.com/search?" q))
#'user/url
user=> url
"http://stackoverflow.com/search?q=clojure+url"
dnolen