Parsing structs with computed fields in JSON.jl v1

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

julia> struct MyStruct
               a::Int
               b::String
               MyStruct(a::Int) = 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}""", MyStruct2) # 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> @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.
...

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

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> @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