# JSON3 and Tuples

**URL:** <https://discourse.julialang.org/t/json3-and-tuples/65699>\
**Category:** New to Julia\
**Tags:** json3\
**Created:** [August 2, 2021, 9:50pm UTC](https://discourse.julialang.org/t/json3-and-tuples/65699 "2021-08-02T21:50:02Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jonm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonm/32/27392_2.png) [@Jonm](https://discourse.julialang.org/u/Jonm)\
**Post date:** [August 2, 2021, 9:50pm UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/1 "2021-08-02T21:50:02Z")

</div>

I am having difficulty using JSON3 and a dictionary with Tuple keys.  
The first example works as expected the second does not. I have tried to use StructTypes to fix this, but I am missing something.

```julia
dString=Dict("p1"=>1,"p2"=>1,"p3"=>2,"p4"=>3)
@show buffS=JSON3.write(dString)
rString=JSON3.read(buffS,Dict{String, Int64})

```

produces

```julia
buffS = JSON3.write(dString) = "{\"p4\":3,\"p3\":2,\"p2\":1,\"p1\":1}"
Dict{String, Int64} with 4 entries:
  "p4" => 3
  "p3" => 2
  "p2" => 1
  "p1" => 1

```

but

```julia
DT=Dict{Tuple{Vararg{String}}, Int64}
@show dTuple=DT(("p1","c2")=>1,("p2","c2")=>1,("p3","c7","b100")=>2)
@show buffT=JSON3.write(dTuple)
rTuple=JSON3.read(buffS,DT)

```

produces

```julia
dTuple = DT(("p1", "c2") => 1, ("p2", "c2") => 1, ("p3", "c7", "b100") => 2) = Dict{Tuple{Vararg{String, N} where N}, Int64}(("p2", "c2") => 1, ("p1", "c2") => 1, ("p3", "c7", "b100") => 2)
buffT = JSON3.write(dTuple) = "{\"(\\\"p2\\\", \\\"c2\\\")\":1,\"(\\\"p1\\\", \\\"c2\\\")\":1,\"(\\\"p3\\\", \\\"c7\\\", \\\"b100\\\")\":2}"
MethodError: Cannot `convert` an object of type Char to an object of type String
Closest candidates are:
  convert(::Type{String}, ::String) at essentials.jl:210
  convert(::Type{T}, ::T) where T<:AbstractString at strings/basic.jl:231
  convert(::Type{T}, ::AbstractString) where T<:AbstractString at strings/basic.jl:232
  ...

Stacktrace:
  [1] setindex!(A::Vector{String}, x::Char, i1::Int64)
    @ Base ./array.jl:839
  [2] copyto!(dest::Vector{String}, src::String)
    @ Base ./abstractarray.jl:846
  [3] _collect
    @ ./array.jl:563 [inlined]
  [4] collect
    @ ./array.jl:561 [inlined]
  [5] _totuple
    @ ./tuple.jl:329 [inlined]
  [6] Tuple
    @ ./tuple.jl:303 [inlined]
  [7] #construct#1
    @ ~/.julia/packages/StructTypes/NJXhA/src/StructTypes.jl:310 [inlined]
  [8] construct
    @ ~/.julia/packages/StructTypes/NJXhA/src/StructTypes.jl:310 [inlined]
  [9] keyvalue
    @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:282 [inlined]
 [10] #read#36
    @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:328 [inlined]
 [11] read
    @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:291 [inlined]
 [12] #read#35
    @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:288 [inlined]
 [13] read
    @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:288 [inlined]
 [14] read(str::String, ::Type{Dict{Tuple{Vararg{String, N} where N}, Int64}}; kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ JSON3 ~/.julia/packages/JSON3/PgKj8/src/structs.jl:34
 [15] read(str::String, ::Type{Dict{Tuple{Vararg{String, N} where N}, Int64}})
    @ JSON3 ~/.julia/packages/JSON3/PgKj8/src/structs.jl:33
 [16] top-level scope
    @ In[125]:4
 [17] eval
    @ ./boot.jl:360 [inlined]
 [18] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
    @ Base ./loading.jl:1094

```

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [August 2, 2021, 9:56pm UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/2 "2021-08-02T21:56:15Z")

</div>

The JSON format only supports string keys, so that won’t work. If you want to use JSON, you can use an array of [key, value] arrays.

---

<div class="post-metadata">

**Author:** ![Jonm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonm/32/27392_2.png) [@Jonm](https://discourse.julialang.org/u/Jonm)\
**Post date:** [August 2, 2021, 10:01pm UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/3 "2021-08-02T22:01:24Z")

</div>

Thanks. That explains it.

I actually have this dictionary as part of a struct (in code someone else wrote so can’t modify that). Is there some way to teach JSON3 to convert this to a [key, value] array and then when it reads it back in convert it to a dictionary. I could do this by hand, but it would be nice if it did this automagically.

Thanks for the help again. I am new to using JSON

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [August 2, 2021, 10:48pm UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/4 "2021-08-02T22:48:15Z")

</div>

I think StructTypes.jl can do that kind of thing ([example](https://juliadata.github.io/StructTypes.jl/stable/#StructTypes.AbstractType)) but I haven’t done it myself.

---

<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:** [August 3, 2021, 2:50am UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/5 "2021-08-03T02:50:30Z")

</div>

I think it’d be easier for you to define the parent struct as a [`CustomStruct`](https://juliadata.github.io/StructTypes.jl/stable/#CustomStruct), which then allows you to define `StructTypes.lower(x::MyStruct) = ...` to change your struct into another object with the representation you want. So for example, if your struct is defined like:

```julia
struct Foo
    id::Int
    vals::Dict{String, Int}
end

```

Then you do the StructTypes definitions like:

```julia
StructTypes.StructType(::Type{Foo}) = StructTypes.CustomStruct()
StructTypes.lower(x::Foo) = (id=x.id, vals=[k => v for (k, v) in x.vals])

```

This changes `vals` to be serialized as an array of key-value pairs, instead of as an object, which is the default for `Dict`.

---

<div class="post-metadata">

**Author:** ![Jonm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonm/32/27392_2.png) [@Jonm](https://discourse.julialang.org/u/Jonm)\
**Post date:** [August 3, 2021, 4:46am UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/6 "2021-08-03T04:46:47Z")

</div>

This is fantastic. Thank you. It worked just as advertised.  
Now I am trying to read the object back in and get the previous dictionary indexed by Tuples.

```julia
struct Foo2
    id::Int
    vals::Dict{Tuple{Vararg{String}}, Int64}
end

StructTypes.StructType(::Type{Foo2}) = StructTypes.CustomStruct()
StructTypes.lower(x::Foo2) = (id=x.id, vals=[k => v for (k, v) in x.vals])

buff=JSON3.write(foo2)
rd=JSON3.read(buff,Foo2)

```

Which produces the following error. I understand that I need to make a constructor for the type Foo2. But I have failed at all attempts so far.

```julia
MethodError: no method matching Foo2(::Dict{String, Any})
Closest candidates are:
  Foo2(::Any, ::Any) at In[9]:2
  Foo2(::Int64, ::Dict{Tuple{Vararg{String, N} where N}, Int64}) at In[9]:2

Stacktrace:
 [1] construct(T::Type, args::Dict{String, Any}; kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ StructTypes ~/.julia/packages/StructTypes/NJXhA/src/StructTypes.jl:310
 [2] construct(T::Type, args::Dict{String, Any})
   @ StructTypes ~/.julia/packages/StructTypes/NJXhA/src/StructTypes.jl:310
 [3] #read#37
   @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:372 [inlined]
 [4] read
   @ ~/.julia/packages/JSON3/PgKj8/src/structs.jl:370 [inlined]
 [5] read(str::String, ::Type{Foo2}; kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ JSON3 ~/.julia/packages/JSON3/PgKj8/src/structs.jl:34
 [6] read(str::String, ::Type{Foo2})
   @ JSON3 ~/.julia/packages/JSON3/PgKj8/src/structs.jl:33
 [7] top-level scope
   @ In[9]:10
 [8] eval
   @ ./boot.jl:360 [inlined]
 [9] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
   @ Base ./loading.jl:1094

```

---

<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:** [August 3, 2021, 5:13am UTC](https://discourse.julialang.org/t/json3-and-tuples/65699/7 "2021-08-03T05:13:00Z")

</div>

So the other piece mentioned in the `CustomStruct` docs, is that you probably want to define a `StructTypes.lowertype` method, so deserialization gives you something better when deserialization; something like:

```julia
StructTypes.lowertype(::Type{Foo2}) = NamedTuple{(:id, :vals), Tuple{Int, Vector{Any}}}

```

then you’ll need a constructor with a signature like:

```julia
function Foo2(id::Int, vals::Vector)
    # convert vals from Vector{Any} to Dict
end

```
