# Parsing structs with computed fields in JSON.jl v1

**URL:** <https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551>\
**Category:** General Usage\
**Tags:** json\
**Created:** [February 10, 2026, 10:29am UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551 "2026-02-10T10:29:50Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [February 10, 2026, 10:29am UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/1 "2026-02-10T10:29:50Z")

</div>

I have the following problem migrating from JSON3.jl to JSON.jl v1:

```julia-repl
julia> struct MyStruct
               a::Int
               b::String
               # MyStruct(a::Int) = new(a, string(a)) this won't work even with JSON3
               MyStruct(a::Int, ::Nothing = nothing) = new(a, string(a))
           end

julia> JSON.parse("""{"a": 42}""", MyStruct)
ERROR: TypeError: in typeassert, expected String, got a value of type Nothing
...

julia> JSON3.read("""{"a": 42}""", MyStruct) # works out of the box
MyStruct2(42, "42")

```

I haven’t been able to figure out how to make this work. There is `StructUtils.@defaults`, which works for construction but not for parsing:

```julia-repl
julia> @defaults struct MyStruct2
                            a::Int
                            b::String = string(a)
                        end

julia> MyStruct2(1)
MyStruct2(1, "1")

julia> JSON.parse("""{"a": 42}""", MyStruct2)
ERROR: UndefVarError: `a` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
...

```

EDIT: corrected mistakes pointed out below

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [February 10, 2026, 12:09pm UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/2 "2026-02-10T12:09:42Z")

</div>

I think JSON.jl wants you to represent such a type with explicit option, a la

```julia-auto
struct MyStruct
a::Int
b::Union{String, Nothing, Missing} #nothing: value not set in json; missing: explicit null but key is present
end

```

If you want to conflate `{"a":1}` and `{"a":1, "b":null}` then you can use simple `Union{Nothing, String}`.

That being said, you should open an issue on StructUtils. The generated default constructors and ` StructUtils.fielddefaults` should ideally have the same behavior on this.

(I fear that this will be not be entirely trivial to get right without breakage, especially side-effects and their ordering of default argument argument evaluation needs to be the same between “default arguments for function / type constructor” and “construction via `JSON.parse` / `StructUtils.fielddefaults`”).

```julia-auto
julia> @macroexpand @defaults struct A
       x::Int
       y::Int = x
       end
quote
    begin
        $(Expr(:meta, :doc))
        struct A
            #= REPL[3]:2 =#
            x::Int
            #= REPL[3]:3 =#
            y::Int
        end
    end
    function A(x)
        #= REPL[3]:1 =#
        return A(x, x)
    end
    StructUtils.fielddefaults(::StructUtils.StructStyle, ::Type{<:A}) = begin
            y = x
            return (; $(Expr(:(=), :y, :y)))
        end
    A
end

```

---

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [February 10, 2026, 1:35pm UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/3 "2026-02-10T13:35:45Z")

</div>

Thanks for the suggestions. Unless I misunderstood, even if I change the field type to allow `Nothing`, I would not get the computed value by parsing. Moreover, I would then need to account for the possibility of `Nothing` in all downstream code, or alternatively convert it to a different type before further use.

So far, I’ve been working around this issue by doing custom `StructUtils.make` functions such as

```julia
function StructUtils.make(::Type{MyStruct2}, x)
    if haskey(x, :b)
        return MyStruct2(x.a, x.b)
    else
        return MyStruct2(x.a, string(x.a))
    end
end

```

i.e. getting rid of the custom internal constructor and pattern matching “by hand”. But this gets tedious if you have dozens of structs you want to serialize, and multiple computed fields, and are changing them frequently. I would love to only need to update the struct definition. For more mature interfaces I’m not opposed to write a shim such that the serializable structure can deviate in more ways from the internal one.

I opened an issue on the JSON repo a while ago: [Parsing structs with computed fields · Issue #430 · JuliaIO/JSON.jl · GitHub](https://github.com/JuliaIO/JSON.jl/issues/430), it might have been user error but it can also be opened on StructUtils.jl.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [February 10, 2026, 3:20pm UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/4 "2026-02-10T15:20:04Z")

</div>

> [@simsurace](#):
>
> Unless I misunderstood

It appears that you understood perfectly fine, and it was me who misunderstood your original question.

> [@simsurace](#):
>
> So far, I’ve been working around this…

If that works, then it should be possible to improve `StructUtils.@defaults` to also generate exactly that `StructUtils.make` for you. Ideally in a PR / upstream, but you can also write your own.

I think this boils down to “`StructUtils.fielddefaults` as a mechanism / abstraction cannot work, scrap it”, which is what I meant with “this may not be entirely trivial to get right without breakage”.

I guess we could summon @quinnj for comment.

---

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [February 10, 2026, 3:48pm UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/5 "2026-02-10T15:48:28Z")

</div>

I don’t know the implementation details too well, but there could maybe also be a stage before mapping 1:1 to fields. Somehow JSON3.jl got it done as well, and I don’t see it mentioned anywhere that JSON.jl v1 intentionally removed that feature.

---

<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:** [February 10, 2026, 5:33pm UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/6 "2026-02-10T17:33:42Z")

</div>

Is the overall code example written out of order, or if not, what is `MyStruct2` at this point?

> [@simsurace](#):
>
> ```julia-auto
> julia> JSON.parse("""{"a": 42}""", MyStruct)
> ERROR: TypeError: in typeassert, expected String, got a value of type Nothing
> ...
> 
> julia> JSON3.read("""{"a": 42}""", MyStruct2) # works out of the box
> MyStruct2(42, "42")
> 
> ```

`JSON3.read("""{"a": 42}""", MyStruct)` throws `MethodError: no method matching MyStruct(::Int64, ::Nothing)` for me.

---

<div class="post-metadata">

**Author:** ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)\
**Post date:** [February 10, 2026, 8:02pm UTC](https://discourse.julialang.org/t/parsing-structs-with-computed-fields-in-json-jl-v1/135551/7 "2026-02-10T20:02:25Z")

</div>

Yeah sorry, my first example is wrong. I got confused between half a dozen ways in which it might work. How I used to address this problem with JSON3:

```julia
struct MyStruct
    a::Int
    b::String
    MyStruct(a::Int, ::Nothing = nothing) = new(a, string(a))
end

```

then

```julia-repl
julia> JSON3.read("""{"a": 42}""", MyStruct)
MyStruct(42, "42")

```

but still

```julia-repl
julia> JSON.parse("""{"a": 42}""", MyStruct)
ERROR: TypeError: in typeassert, expected String, got a value of type Nothing
...

```
