Idiomatically accessing json objects with clojurescript
Asked Answered
I

5

18

Anyone have any docs for idiomatic clojurescript for access a javascript object (returned as json, essentially a hash)?

I have a JSON object returned via an AJAX request:

{
  list: [1,2,3,4,5],
  blah: "vtha",
  o: { answer: 42 }
}

How do I access these fields using clojurescript?

I can do:

(.-list data)

But how does this work when I have nested values and objects?

(.-answer (.-o data))

The above seems to be quite clumsy, especially given the nice js syntax of: data.o.answer.

What is the idiomatic way of accessing json objects with clojurescript?

Note:

I realised that I can actually refer to elements using JS syntax, which is quite handy actually. So the following will work correctly:

(str data.o.answer)
Incurious answered 22/3, 2012 at 10:55 Comment(3)
I assume in your actual setup, you get proper JSON?Itemize
So far I think the answer is really: there is no idiomatic way of doing this (yet).Incurious
Last response was a while ago -- is (str data.o.answer) idiomatic?Sunsunbaked
R
17

You probably want aget:

(aget foo "list")

aget isn't variadic yet, but hopefully will be soon it's variadic now. (aget data "o" "answer") would work

Romeo answered 22/3, 2012 at 16:26 Comment(2)
For the record, it's variadic now. (aget data "o" "answer") would work.Trunk
FWIW, David would retract this answer these days, and suggest instead (goog.object/get foo "list"). See more in the answer by Jiacai Liu.Manganite
S
11

Firstly, your proposed syntax for nested access does work:

ClojureScript:cljs.user> (def data 
    (JSON/parse "{\"list\": \"[1,2,3,4,5]\", \"blah\": \"vtha\", \"o\": {\"answer\": \"42\"}}"))
#<[object Object]>
ClojureScript:cljs.user> (.-answer (.-o data))
"42"

You can use the threading macros...

ClojureScript:cljs.user> (-> data (.-o) (.-answer))
"42"

Or .. notation

ClojureScript:cljs.user> (.. data -o -answer)
"42"
Stefanstefanac answered 22/3, 2012 at 11:7 Comment(3)
Does indeed work, still seems clunky to me, even with threading macros.Incurious
the parens in the threading are redundant (-> data .-o .-answer)Jock
@Jock these days I'd prefer to use (.. data -o -answer), as noted by @stephen-nelson - i'll update the answerStefanstefanac
W
6

If you're dealing with any amount of data, I'd convert the JSON into clojure data structures and then use the usual idioms:

(let [my-json (js* "{
                     list: [1,2,3,4,5],
                     blah: \"vtha\",
                     o: { answer: 42 }
                   }")
      converted (js->clj my-json)]

  (get-in converted ["list" 3]) ;; => 4
  (-> converted "o" "answer") ;;=> 42
)

(Note: don't use js* if you can help it; it's not idiomatic and might go away in future versions of ClojureScript.)

Whaleboat answered 22/3, 2012 at 14:39 Comment(3)
I'm finding that the data is converted into a vector rather than a hash, so actually more co,plicated to extract data from than the default js objectIncurious
Actually, the converted data seems to vary unpredictably ... obj map or vectorIncurious
This is like buying the whole grocery store just to get the oranges. A waste of resources.Oesophagus
P
2

Clojurescript has a .. operator that is useful for chained javascript calls:

(.. data -o -answer) => data.o.answer => 42
(aget (.. data -list) 1) => data.list[1] => 2

You can use most list operators on arrays too, e.g.

(into [] (.. data -list)) ; vector [1 2 3 4]
Prink answered 26/4, 2013 at 23:53 Comment(0)
G
2

Forget about aget, it's mainly designed for array (array get). Use

  • goog.object/get
  • goog.object/set

See more

Gullah answered 29/1, 2018 at 13:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.