I have a found a plugin that enables Vim to parse JSON. I need to export VimScript dictionaries as JSON. Currently I am just using:
let str = string(dict)
substitute(str, "'", '"', 'g')
This is working but is bound to break when I run into dictionaries with embedded quotes. What is a better way?
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()
.
Since Vim 7.4.1304 (so definitely available with Vim 8.0), these functions are built into Vim as json_encode()
and json_decode()
.
For backwards compatibility with even older Vim versions, the WebAPI.vim plugin has a JSON parser and encoder implemented in pure Vimscript:
:let jsonString = webapi#json#encode({...})
I wrote a little function to echo
the json string, with "
double quote escaped. not sure if it works for your needs:
function! ToJson(input)
if type(a:input) == type({})
echo "{"
let di = 0
for key in keys(a:input)
let di += 1
if type(key) == type('')
echo '"'.substitute(key, '"', '\\"','g').'":'
else
echo '"'.key.'":'
endif
call ToJson(a:input[key])
echo di<len(a:input)? "," : ""
endfor
echo "}"
elseif type(a:input) == type([])
echo "["
let li = 0
for e in a:input
let li += 1
call ToJson(e)
if li<len(a:input)
echo ","
endif
endfor
echo "]"
elseif type(a:input) == type('')
echo '"'.substitute(a:input, '"', '\\"','g').'"'
else
echo '"'.a:input.'"'
endif
endfunction
a dict like:
let d1={'one':'"""', 'two':123, 333:['11',22,'"_"_"'], 'four':"''"}
will be output as:
{
"four":
"''"
,
"one":
"\"\"\""
,
"two":
"123"
,
"333":
[
"11"
,
"22"
,
"\"_\"_\""
]
}
I didn't do much debug/testing. also the format looks not nice, but I guess you don't care about the format since you used string()
...
the above output could be formatted into (by an online json formatter):
{
"four":"''",
"one":"\"\"\"",
"two":"123",
"333":[
"11",
"22",
"\"_\"_\""
]
}
hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With