Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Namespaces and vars by

I'm completely new with Clojure and I'm trying to do some basic stuff with Slurp and JSON.

I found some code (doesn't work) that I'm using as an example to try to learn some basics.
The following code is supposed to get some JSON data from Ipinfo.io and then pull JUST the City name from it and print it.
I'm getting so many errors with the namespaces that I can't even get to testing the actual code. :-(

Any help would be GREATLY appreciated ! :-)
Jason

(ns my-namespace

(:require [clojure.core :as core]
			[clojure.data.json :as json]))

(defn get-city [ip-addr]

(let [resp (core/slurp (str "https://ipinfo.io" ip-addr "/json"))
	city-name (->> resp 
			(json/read-str)
			:city)]
(core/prn city-name)
city-name))

1 Answer

0 votes
by

You're almost there! Few things:

  1. you don't need to require clojure.core, that's auto-required in every namespace, so just:

    (ns my-namespace (:require [clojure.data.json :as json]))

is sufficient.

  1. You seem to be missing the / after the host name.

  2. By default data.json will read json as string keys, so everything above worked, except the :city part. It helps in Clojure to work interactively with a REPL and do one step at a time - you could then quickly see that read-str returned a map with string keys.

  3. To find docs for this function, use (doc json/read-str) to see the docstring, which refers to read where this is documented: (doc json/read). There is a :key-fn option which defaults to identity but you can use keyword function to construct keyword keys instead. Once you do that, you'll want to use -> instead of ->> to thread first.

Putting it all together:

(ns my-namespace
  (:require [clojure.data.json :as json]))

(defn get-city [ip-addr]
  (let [resp (slurp (str "https://ipinfo.io/" ip-addr "/json"))
        city-name (-> resp 
                      (json/read-str :key-fn keyword)
                      :city)]
    city-name))
by
Trying this but still get ns errors. this is what I get...

user=> (ns my-namespace (:require [clojure.data.json :as json]))
Execution error (FileNotFoundException) at my-namespace/eval138$loading (REPL:1).
Could not locate clojure/data/json__init.class, clojure/data/json.clj or clojure/data/json.cljc on classpath.
by
This code requires the data.json library on the classpath. Dependency info can be found at https://github.com/clojure/data.json/#releases-and-dependency-information but I would need to know more about how you're starting your repl to know what you need to do to add it.
by
I just started the repl with  clj.
I should probably be using lein
by
Ok!! I got it running by starting clj with:
clj -Sdeps '{:deps {org.clojure/data.json {:mvn/version "2.4.0"}}}'

Some problems with the program still...
experimenting...
by
I'm breaking it down:
This works:
(print (slurp (str "https://ipinfo.io/" ip "/json")))

But I can't seem to pass the IP in properly if I create a function:
(defn mytest [ip]
(print (slurp (str "https://ipinfo.io/" ip "/json"))) )

mytest 24.80.129.110
#object[my_namespace$mytest 0x2e8a1ab4 "my_namespace$mytest@2e8a1ab4"]
Syntax error reading source at (REPL:18:0).
Invalid number: 24.80.129.110

mytest "24.80.129.110"
#object[my_namespace$mytest 0x2e8a1ab4 "my_namespace$mytest@2e8a1ab4"]
"24.80.129.110"
by
Function invocation in Clojure always involves surrounding with parens, and the ip will have to be a string to be a valid value, so:

(mytest "24.80.129.110")
by
OMG! I can't believe I did that!
Thank you!
...