Why does Julia not support JSON syntax to create a Dict?

Macros can only operate on syntactically valid Julia code, since they always operate on expression objects. Single quotes (') in Julia always denote single-character literals, so something like 'abc' is not valid syntax, so this will error during parsing. Disallowing multi-character strings enclosed with single quotes was a conscious decision, because otherwise you would get parsing ambiguities, since ' is also a postfix operator for adjoint. For this specific case, a non-standard string literal would probably make more sense.

5 Likes

Thanks for the explanation. Did I understood you correctly that:

@js { d: 'thirty', 123 } 

is not possible, but

js"{ d: 'thirty', 123 }" 

could be?

4 Likes

Yes! It should be as simple as:

using JSON3
macro js_str(s)
    return :(JSON3.read($s))
end
4 Likes

Thanks. Pretty cool! However, the same problem persists:

julia> using JSON3

julia> macro js_str(s)
           return :(JSON3.read($s))
       end
@js_str (macro with 1 method)

julia> js"{\"a_number\" : 5.0, \"an_array\" : [\"string\", 9]}"
Dict{String,Any} with 2 entries:
  "an_array" => Any["string", 9]
  "a_number" => 5.0

julia> js"{'a_number' : 5.0, 'an_array' : ['string', 9]}"
ERROR: Invalid object key
Line: 0
Around: ...{'a_number' : 5.0, 'an...
            ^

Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] _error(::String, ::JSON.Parser.MemoryParserState) at C:\Users\tfr004\.julia\packages\JSON\d89fA\src\Parser.jl:142
 [3] parse_object(::JSON.Parser.ParserContext{Dict{String,Any},Int64,true,nothing}, ::JSON.Parser.MemoryParserState) at C:\Users\tfr004\.julia\packages\JSON\d89fA\src\Parser.jl:227
 [4] parse_value(::JSON.Parser.ParserContext{Dict{String,Any},Int64,true,nothing}, ::JSON.Parser.MemoryParserState) at C:\Users\tfr004\.julia\packages\JSON\d89fA\src\Parser.jl:168
 [5] #parse#1(::Type, ::Type{Int64}, ::Bool, ::Nothing, ::typeof(JSON.Parser.parse), ::String) at C:\Users\tfr004\.julia\packages\JSON\d89fA\src\Parser.jl:463
 [6] parse(::String) at C:\Users\tfr004\.julia\packages\JSON\d89fA\src\Parser.jl:461
 [7] top-level scope at REPL[16]:1

julia>

Possibly because 'key' is not valid JSON syntax. You need "key". And then probably you want """ to make your life easier, eg

julia> js"""{"a_number" : 5.0, "an_array" : ["string", 9]}"""
JSON3.Object{Base.CodeUnits{UInt8,String},Array{UInt64,1}} with 2 entries:
  :a_number => 5
  :an_array => Any["string", 9]
2 Likes

Have a look at Simon’s JSServe.jl

https://github.com/SimonDanisch/JSServe.jl

It might be of some interest :slight_smile:

Try this

julia> js"""
       {
           "a_number" : 5.0,
           "an_array" : ["string", 9]
       }
       """
JSON3.Object{Base.CodeUnits{UInt8,String},Array{UInt64,1}} with 2 entries:
  :a_number => 5
  :an_array => Any["string", 9]

:slight_smile:

Edit: If you really wanted a Dict, just do this

julia> macro js_str(s)
           return :(Dict(JSON3.read($s)))
       end
@js_str (macro with 1 method)

julia> js"""
       {
           "a_number" : 5.0,
           "an_array" : ["string", 9]
       }
       """
Dict{Symbol,Any} with 2 entries:
  :a_number => 5
  :an_array => Any["string", 9]

:nerd_face:

2 Likes

Joining late…
I’ve asked about this separately.

This is the code I can up with. Maybe it’s of use to someone…


json(n) = n
json(n::Symbol) = esc(n)

function json(n::Expr)
    if n.head == :vect
        return Expr(:vect, map(json, n.args)...)
    elseif n.head == :braces
        kv = []
        for f in n.args
            f isa Expr || error("not a valid JSON")
            f.args[1] == :(:) || error("not a valid JSON")
            k = f.args[2]

            typeof(k) ∈ (Symbol, String) || error("not a valid JSON")
            k = string(k)
            v = json(f.args[3])
            push!(kv, :($k=>$v))
        end
        return :(Dict{String, Any}($(kv...)))
    else
        return esc(n)
    end
end

macro json(n)
    return json(n)
end

function aa()
    i = 2
    j = 3
    return @json({
                a : {  # recursive...
                    b : i, # accessing local variable
                    e : nothing,
                }, "c": [1, 2.0, "mixed types"], # array
                "d": i + j # expression
            })
end

aa()
#Dict{String, Any} with 3 entries:
#  "c" => Any[1, 2.0, "mixed types"]
#  "a" => Dict{String, Any}("e"=>nothing, "b"=>2)
#  "d" => 5

Thanks,
DD

2 Likes