I am not sure whether this should be a separate answer, an edit to @Kent's answer, or a comment on @Kent's answer. Here is a version of @Kent's function with a few simplifications:
function! ToJson(input)
let json = ''
if type(a:input) == type({})
let json .= "{"
let di = 0
for key in keys(a:input)
let di += 1
let json .= '"'.escape(key, '"').'":'
let json .= ToJson(a:input[key])
let json .= di<len(a:input)? "," : ""
endfor
let json .= "}"
elseif type(a:input) == type([])
let json .= "["
let li = 0
for e in a:input
let li += 1
let json .= ToJson(e)
if li<len(a:input)
let json .= ","
endif
endfor
let json .= "]"
else
let json .= '"'.escape(a:input, '"').'"'
endif
return json
endfunction
Instead of echoing the result, this returns it as a String. Also, I use escape()
instead of substitute()
. Finally, I think (Check this!) that it is safe to use escape()
as I did without first checking that the argument is a String.
Here is a shorter version, using map()
. I do not know whether depth of recursion matters more for this version or the others, but if your input is big enough for that to matter, it probably goes faster if we let map()
handle the recursion.
function! ToJson(input)
let json = ''
if type(a:input) == type({})
let parts = copy(a:input)
call map(parts, '"\"" . escape(v:key, "\"") . "\":" . ToJson(v:val)')
let json .= "{" . join(values(parts), ",") . "}"
elseif type(a:input) == type([])
let parts = map(copy(a:input), 'ToJson(v:val)')
let json .= "[" . join(parts, ",") . "]"
else
let json .= '"'.escape(a:input, '"').'"'
endif
return json
endfunction
Using either version, I get the same result as @Kent's function, except for whitespace. I have not tested it with anything more complex than @Kent's d1
. It might be safer to use deepcopy()
than copy()
.