# JSON3 read \`Dict{Tuple{Int,Int}, T}\` unexpected behavior

**URL:** <https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759>\
**Category:** Data\
**Tags:** json3\
**Created:** [August 2, 2024, 7:43am UTC](https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759 "2024-08-02T07:43:13Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![guimarqu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/guimarqu/32/211057_2.png) [@guimarqu](https://discourse.julialang.org/u/guimarqu)\
**Post date:** [August 2, 2024, 7:43am UTC](https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759/1 "2024-08-02T07:43:13Z")

</div>

Hey,

I’m using JSON3.jl in a project and I found this unexpected behavior when reading `Dict{Tuple{Int,Int}, T}`.  
I don’t know if I’m doing something wrong or if there is just a bug in the package but this is a quite classic operation so I thought it would work straight away.

Here is a simple example that reproduces the bug :

```julia
using StructTypes, JSON3

struct A
  a::Dict{Tuple{Int,Int}, Char}
end

StructTypes.StructType(::Type{A}) = StructTypes.Struct()

a = A(Dict((1, 2) => 'a', (2, 4) => 'b', (15, 20) => 'c'))

@show JSON3.read(JSON3.write(a), A)

```

The `write` operation works well. You get this JSON:

```julia
"{\"a\":{\"(2, 4)\":\"b\",\"(1, 2)\":\"a\",\"(15, 20)\":\"c\"}}"

```

However, when you read the output of `write` you get the following result:

```julia
A(Dict((40, 50) => 'b', (40, 49) => 'c'))

```

I’m pretty sure nobody would expect this behavior.  
Has anyone experienced this problem and know how to fix it?

Best,

---

<div class="post-metadata">

**Author:** ![guimarqu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/guimarqu/32/211057_2.png) [@guimarqu](https://discourse.julialang.org/u/guimarqu)\
**Post date:** [August 2, 2024, 9:29am UTC](https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759/2 "2024-08-02T09:29:26Z")

</div>

Ok I think I found the root of the problem:

```julia
julia> JSON3.write((1, 2))
"[1,2]"

```

I’ll edit this answer if I find a solution :

```julia
function parsetuple(s::AbstractString)
    r = match(r"\(([0-9]+), *([0-9]+)\)", s)
    return (parse(Int, r.captures[1]), parse(Int, r.captures[2]))
end
StructTypes.StructType(::Type{Tuple{Int,Int}}) = StructTypes.StringType()
StructTypes.construct(::Type{Tuple{Int,Int}}, x::String; kw...) = parsetuple(x)

```

trick if you’re using JSON3 just for debugging purposes.  
It should no go in prod as `StructTypes.StructType(::Type{Tuple{Int,Int}})` is already defined and returns `ArrayType()`.

or define a new constructor and convert the string keys into the desired type.

---

<div class="post-metadata">

**Author:** ![ericphanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ericphanson/32/215186_2.png) [@ericphanson](https://discourse.julialang.org/u/ericphanson)\
**Post date:** [August 2, 2024, 12:10pm UTC](https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759/3 "2024-08-02T12:10:18Z")

</div>

I think the main problem is that JSON keys must be strings, so it is writing `(1,2)` as a string `"(1,2)"` to use as the key, which does not preserve the actual meaning of the tuple.

I think it is a bad bug that you get

> ```julia
> A(Dict((40, 50) => 'b', (40, 49) => 'c'))
> 
> ```

back, it should error instead.

edit: filed [`JSON3.read(str, MyStruct)` does not roundtrip correctly nor error · Issue #285 · quinnj/JSON3.jl · GitHub](https://github.com/quinnj/JSON3.jl/issues/285)

---

<div class="post-metadata">

**Author:** ![Naunet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/naunet/32/47690_2.png) [@Naunet](https://discourse.julialang.org/u/Naunet)\
**Post date:** [August 2, 2024, 1:31pm UTC](https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759/4 "2024-08-02T13:31:34Z")

</div>

There is a similar issue about using Structs as keys in the package already : [Reading and writing is broken for `Dict`s with struct keys · Issue #71 · quinnj/JSON3.jl · GitHub](https://github.com/quinnj/JSON3.jl/issues/71).  
JSON only allows string keys unfortunately.

---

<div class="post-metadata">

**Author:** ![guimarqu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/guimarqu/32/211057_2.png) [@guimarqu](https://discourse.julialang.org/u/guimarqu)\
**Post date:** [August 8, 2024, 3:24pm UTC](https://discourse.julialang.org/t/json3-read-dict-tuple-int-int-t-unexpected-behavior/117759/5 "2024-08-08T15:24:33Z")

</div>

Hey,

A solution to my problem (I think much better than the quick workaround I wrote last week)

```julia
using JSON3, StructTypes

struct A
    edges::Dict{Tuple{Int,Int}, String}
end

StructTypes.StructType(::Type{A}) = StructTypes.CustomStruct()

struct _JSONEdge
    src::Int
    dst::Int
    label::String
end

StructTypes.StructType(::Type{_JSONEdge}) = StructTypes.Struct()

struct _JSONA
    edges::Vector{_JSONEdge}
end

StructTypes.StructType(::Type{_JSONA}) = StructTypes.Struct()

_JSONA(a::A) = _JSONA([_JSONEdge(src, dst, label) for ((src, dst), label) in a.edges])

StructTypes.lowertype(::Type{A}) = _JSONA
StructTypes.lower(a::A) = _JSONA(a)

A(json::_JSONA) = A(Dict((edge.src, edge.dst) => edge.label for edge in json.edges))

a = A(Dict((1,2) => "a", (2,3) => "b"))
json = JSON3.write(a)
b = JSON3.read(json, A)

```
