# JSON type serialization

**URL:** <https://discourse.julialang.org/t/json-type-serialization/9794>\
**Category:** General Usage\
**Created:** [March 18, 2018, 6:18pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794 "2018-03-18T18:18:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [March 18, 2018, 6:18pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/1 "2018-03-18T18:18:40Z")

</div>

Hi,

I wonder why serialisation of JSONs does not contain information about type? Has anyone encountered similar need?

For example

```julia
struct TestType{A}
  a::A
end

println(JSON.json(TestType("Hello")))
{"a":"Hello"}

```

Does not tell me anything about serialised type being TestType?

Thanks for help

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 19, 2018, 5:24am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/2 "2018-03-19T05:24:14Z")

</div>

You can change the way a particular type is serialized by implementing the `JSON.lower()` method:

```julia
julia> JSON.lower(t::TestType) = Dict("type"=>"TestType", "a"=>t.a)

julia> println(JSON.json(TestType("Hello")))
{"type":"TestType","a":"Hello"}

```

if you want more full-featured serialization of complex data types in Julia, you might want to check out [https://github.com/simonster/JLD2.jl](https://github.com/simonster/JLD2.jl)

---

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [March 19, 2018, 7:50am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/3 "2018-03-19T07:50:23Z")

</div>

Thanks a lot.

---

<div class="post-metadata">

**Author:** ![anon67531922](https://avatars.discourse-cdn.com/v4/letter/a/48db29/32.png) [@anon67531922](https://discourse.julialang.org/u/anon67531922)\
**Post date:** [March 19, 2018, 3:17pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/4 "2018-03-19T15:17:36Z")

</div>

You can also check out [https://github.com/quinnj/JSON2.jl](https://github.com/quinnj/JSON2.jl)

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [March 19, 2018, 3:56pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/5 "2018-03-19T15:56:54Z")

</div>

Cool! I didn’t know that existed.

---

<div class="post-metadata">

**Author:** ![quinnj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/quinnj/32/11_2.png) [@quinnj](https://discourse.julialang.org/u/quinnj)\
**Post date:** [March 20, 2018, 4:24am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/6 "2018-03-20T04:24:07Z")

</div>

JSON is typically considered an “anonymous type” kind of specification, so there isn’t really a notion of what “type” an object is. More specifically, there are only a limited set of “types” in JSON:

- Object: specified by `{ ... }` brackets and key-value pairs, w/ keys being strings
- Array: specified by `[...]` brackets, each element being separated by a comma
- String: specified by `" ... "` double quotation marks
- Number: either integer or float values
- Null: the literal `null` value

In this schema, as I mentioned, the closest kind of thing to a custom Julia type is an Object, so the natural representation of your `TestType` example is indeed `{"a":"Hello"}`. As you’ve noticed, this can pose challenges w/ a type like `TestType{A}` in Julia, where the inner field is a parameter and can essentially be one of many types.

In JSON2, this is made a bit simpler by allowing the user to specify the type that they wish to deserialize, like the following example:

```julia
julia> struct TTT{A}
       x::A
       end

julia> a = TTT(1.0)
TTT{Float64}(1.0)

julia> b = TTT("hey")
TTT{String}("hey")

julia> aa = JSON2.write(a)
"{\"x\":1.0}"

julia> bb = JSON2.write(b)
"{\"x\":\"hey\"}"

julia> JSON2.read(aa, TTT{Float64})
TTT{Float64}(1.0)

julia> JSON2.read(bb, TTT{String})
TTT{String}("hey")

```

In this case, I’m able to read and write our custom struct `TTT` with either a Float64 or String inner field because when reading, we’re able to specify the exact type we want to construct, and JSON2 can, using julia’s powerful reflection tools, figure out how to construct one of those types.

---

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [March 20, 2018, 6:21am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/7 "2018-03-20T06:21:49Z")

</div>

Hi Jacob,

this is nice. What would happen if the specification of TTT contains one more element indicating the type?

Can I ask what are the areas where JSON2 is better than JSON?

Last but not least, I cannot install it. I am getting following error:

```julia
Pkg.add("JSON2")
ERROR: JSON2 can't be installed because it has no versions that support 0.6.2 of julia. You may need to update METADATA by running `Pkg.update()`

```

Thanks for replys.

Tomas

---

<div class="post-metadata">

**Author:** ![quinnj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/quinnj/32/11_2.png) [@quinnj](https://discourse.julialang.org/u/quinnj)\
**Post date:** [March 20, 2018, 3:34pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/8 "2018-03-20T15:34:38Z")

</div>

Yeah, sorry, JSON2 is 0.7 only for now, since it relies on new features coming w/ the new release. I haven’t publicized the package much.

---

<div class="post-metadata">

**Author:** ![ScottPJones](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/scottpjones/32/146_2.png) [@ScottPJones](https://discourse.julialang.org/u/ScottPJones)\
**Post date:** [March 20, 2018, 4:25pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/9 "2018-03-20T16:25:09Z")

</div>

Bool also, `true` and `false`

---

<div class="post-metadata">

**Author:** ![ScottPJones](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/scottpjones/32/146_2.png) [@ScottPJones](https://discourse.julialang.org/u/ScottPJones)\
**Post date:** [March 20, 2018, 4:29pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/10 "2018-03-20T16:29:22Z")

</div>

JSON2’s functionality is very intriguing - I’m also very much looking forward for it to be “officially” released by Jacob! (If you are looking for other good data oriented packages for Julia, Jacob’s ones are a good place to start,  
such as CSV.jl also)

---

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [March 21, 2018, 2:57pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/11 "2018-03-21T14:57:31Z")

</div>

Can I ask how JSON2 will deserialize array? JSON deserialize every array to ANY, which is quite inconvenient.

```julia
julia> JSON.parse("""{"a": [1,2,3,4]}""")
Dict{String,Any} with 1 entry:
  "a" => Any[1, 2, 3, 4]

```

Thanks for an answer.

Tomas

---

<div class="post-metadata">

**Author:** ![samoconnor](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/samoconnor/32/1802_2.png) [@samoconnor](https://discourse.julialang.org/u/samoconnor)\
**Post date:** [March 26, 2018, 10:46pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/12 "2018-03-26T22:46:24Z")

</div>

With [LazyJSON](https://discourse.julialang.org/t/announce-a-different-way-to-read-json-data-lazyjson-jl/9046), it will seem that you get an `AbstractVector{Any}`. However, because of the lazy implementation, no vector has actually been created yet. So, if you then `convert` to a desired vector type, the vector will be constructed by directly parsing the JSON text (there is no intermediate `Vector{Any}` representation).

```julia
julia> using LazyJSON
LazyJSON.
julia> x = LazyJSON.value("""{"a": [1,2,3,4]}""")
LazyJSON.Object{String} with 1 entry:
  "a" => Any[1, 2, 3, 4]

julia> v = convert(Vector{Int}, x.a)
4-element Array{Int64,1}:
 1
 2
 3
 4

```

---

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [March 27, 2018, 4:48am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/13 "2018-03-27T04:48:06Z")

</div>

Thanks Sam.

I have on the end found that I really need to keep Any, because JSONs are wild and if typing is too strict, it might happen that I would create too tight dictionaries.

Tomas

---

<div class="post-metadata">

**Author:** ![Vincent\_Picaud](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vincent_picaud/32/3108_2.png) [@Vincent\_Picaud](https://discourse.julialang.org/u/Vincent_Picaud)\
**Post date:** [May 6, 2026, 11:45am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/14 "2026-05-06T11:45:05Z")

</div>

I just ran into this issue and wrote a small blog post with a minimal  
example that saves type information and reuse it during  
deserialization. Hope it helps!

Minimal working example :

```julia-auto
using JSON
using StaticArrays
 
abstract type Abstract_Foo end
 
@nonstruct struct Foo_A{V <: AbstractVector} <: Abstract_Foo
    v::V
    x::Float64
end
 
@nonstruct struct Foo_B <: Abstract_Foo
    v::AbstractVector
    n::Int
end
 
function StructUtils.lower(to_serialize::Foo_A)
 
    return (type = string(typeof(to_serialize)),
            v = to_serialize.v,
            x = to_serialize.x)
end
 
function StructUtils.lower(to_serialize::Foo_B)
 
    return (type = string(typeof(to_serialize)),
            v_type = string(typeof(to_serialize.v)),
            v = to_serialize.v,
            n = to_serialize.n)
end
 
a = Foo_A(@SVector(Int[1,2]),1.2)
 
a_json_str = JSON.json(a, pretty=true)
 
println(a_json_str)
 
b = Foo_B(Float16[3,4],34)
 
b_json_str = JSON.json(b, pretty=true)
 
println(b_json_str)
 
function StructUtils.lift(type::Type{<:Abstract_Foo},
                          to_deserialize)
 
    actual_type = Base.eval(Main,Meta.parse(to_deserialize.type))
    StructUtils.lift(actual_type,to_deserialize)
end
 
function StructUtils.lift(type::Type{<:Foo_A{V}},
                          to_deserialize) where {V<:AbstractVector}
 
    v = StructUtils.lift(V,to_deserialize.v) # deserialize vect.
    x = to_deserialize.x
 
    type(v,x)
end
 
function StructUtils.lift(type::Type{<:Foo_B},
                          to_deserialize)
 
    v_type = Base.eval(Main,Meta.parse(to_deserialize.v_type))
    v = StructUtils.lift(v_type,to_deserialize.v) # deserialize vect.
    n = to_deserialize.n
 
    type(v,n)
end
 
JSON.parse(a_json_str,Abstract_Foo)
 
JSON.parse(b_json_str,Abstract_Foo)

```

Blog post containing some explanations : [WordPress: JSON & custom serialization](https://pixorblog.wordpress.com/2026/05/05/julia-custom-serialization-with-json-jl/)

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 6, 2026, 9:43pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/15 "2026-05-06T21:43:24Z")

</div>

Do we have a Julia type-specific parser anywhere? The `eval`-`Meta.parse` approach is vulnerable to JSON injection:

```julia-auto
julia> JSON.parse(a_json_str,Abstract_Foo)
Foo_A{SVector{2, Int64}}([1, 2], 1.2)

julia> bad_a = "{\n \"type\": \"println(rand()); Foo_A{SVector{2, Int64}}\",\n \"v\": [\n 1,\n 2\n],\n \"x\": 1.2\n}"
"{\n \"type\": \"println(rand()); Foo_A{SVector{2, Int64}}\",\n \"v\": [\n 1,\n 2\n],\n \"x\": 1.2\n}"

julia> JSON.parse(bad_a,Abstract_Foo)
0.8773892447439415
Foo_A{SVector{2, Int64}}([1, 2], 1.2)

```

---

<div class="post-metadata">

**Author:** ![langestefan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/langestefan/32/207923_2.png) [@langestefan](https://discourse.julialang.org/u/langestefan)\
**Post date:** [May 6, 2026, 9:48pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/16 "2026-05-06T21:48:19Z")

</div>

There is [Home · JSON3.jl](https://quinnj.github.io/JSON3.jl/stable/#Generated-Types) if that’s what you mean. Unfortunately JSON3.jl is deprecated in favour of JSON.jl, which does not yet have this functionality.

---

<div class="post-metadata">

**Author:** ![Vincent\_Picaud](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vincent_picaud/32/3108_2.png) [@Vincent\_Picaud](https://discourse.julialang.org/u/Vincent_Picaud)\
**Post date:** [May 6, 2026, 9:57pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/17 "2026-05-06T21:57:51Z")

</div>

Yes, I previously used JSON3.jl, but its deprecation was the reason I switched to JSON.jl.

---

<div class="post-metadata">

**Author:** ![Vincent\_Picaud](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vincent_picaud/32/3108_2.png) [@Vincent\_Picaud](https://discourse.julialang.org/u/Vincent_Picaud)\
**Post date:** [May 6, 2026, 10:06pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/18 "2026-05-06T22:06:02Z")

</div>

Thank you for the example. There is indeed a clear issue at that level. Ideally, a Julia type-specific parser would be preferable as you mentioned.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 6, 2026, 11:00pm UTC](https://discourse.julialang.org/t/json-type-serialization/9794/19 "2026-05-06T23:00:32Z")

</div>

Never used that because I only parse JSON to tables in practice, but that seems different from Vincent\_Picaud’s parsing of pre-existing type _names_, not new structure. Anything that can restrict a string or parsed `Symbol`/`Expr` to 1 possibly parametric type name with no nested function calls _might_ stop the bad effects of JSON injection (not sure because some syntax lower to a few function calls). Checking for a header of `:curly` isn’t enough for even that:

```julia-auto
julia> bad_a2 = "{\n \"type\": \"Foo_A{SVector{(println(rand()); 2), Int64}}\",\n \"v\": [\n 1,\n 2\n],\n \"x\": 1.2\n}"
"{\n \"type\": \"Foo_A{SVector{(println(rand()); 2), Int64}}\",\n \"v\": [\n 1,\n 2\n],\n \"x\": 1.2\n}"

julia> Meta.parse("Foo_A{SVector{(println(rand()); 2), Int64}}").head
:curly

julia> JSON.parse(bad_a2,Abstract_Foo)
0.8077058315234483
Foo_A{SVector{2, Int64}}([1, 2], 1.2)

```

---

<div class="post-metadata">

**Author:** ![Vincent\_Picaud](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vincent_picaud/32/3108_2.png) [@Vincent\_Picaud](https://discourse.julialang.org/u/Vincent_Picaud)\
**Post date:** [May 7, 2026, 10:06am UTC](https://discourse.julialang.org/t/json-type-serialization/9794/20 "2026-05-07T10:06:51Z")

</div>

> [@Benny](#):
>
> ```julia-auto
> julia> Meta.parse("Foo_A{SVector{(println(rand()); 2), Int64}}").head
> :curly
> 
> ```

The best I’ve found so far is to check whether the parsed expression contains a `:call`.

```julia-auto
  function contains_call(ex)
      if ex isa Expr
          ex.head == :call && return true

          for arg in ex.args
              contains_call(arg) && return true
          end
      end

      return false
  end

  function annotated_parse(s::String)
      ex = Meta.parse(s)
      dangerous = contains_call(ex)
      (ex=ex,dangerous=dangerous)
  end

```

These tests are ok :

```julia-auto
  @assert annotated_parse("123").dangerous == false
  @assert annotated_parse("Foo_A{SVector{2, Int64}}").dangerous == false
  @assert annotated_parse("@NamedTuple{a::Int64, b::String}").dangerous == false

  @assert annotated_parse("rand()").dangerous == true
  @assert annotated_parse("run(`ls`)").dangerous == true
  @assert annotated_parse("ccall(:system, Cint, (Cstring,), \"ls\")").dangerous == true
  @assert annotated_parse("SVector{(rand(); 2), Int64}").dangerous == true

```

Unfortunately broadcasting

```julia-auto
annotated_parse("x=rand.(2)")

```

is **not** detected as dangerous… I have to think about that…

A quick fix is

```julia-auto
  function contains_call(ex)
      if ex isa Expr
          if ex.head in (:call, :macrocall, :.)
              return true
          end

          for arg in ex.args
              contains_call(arg) && return true
          end
      end

      return false
  end

```

but this time `@NamedTuple{a::Int64, b::String}` is considered as dangerous…

[Next page](https://discourse.julialang.org/t/json-type-serialization/9794.md?page=2)
