# StructTypes.Mutable() does not call the inner constructor

**URL:** <https://discourse.julialang.org/t/structtypes-mutable-does-not-call-the-inner-constructor/102118>\
**Category:** General Usage\
**Tags:** question\
**Created:** [July 26, 2023, 12:33pm UTC](https://discourse.julialang.org/t/structtypes-mutable-does-not-call-the-inner-constructor/102118 "2023-07-26T12:33:23Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![BlondDon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blonddon/32/51819_2.png) [@BlondDon](https://discourse.julialang.org/u/BlondDon)\
**Post date:** [July 26, 2023, 12:33pm UTC](https://discourse.julialang.org/t/structtypes-mutable-does-not-call-the-inner-constructor/102118/1 "2023-07-26T12:33:23Z")

</div>

```julia
using JSON3
using StructTypes

Base.@kwdef mutable struct MyType2
    c::Float64 = 3.0

    MyType2(c) = c < 0.0 ? error("bar") : new(c)
end

Base.@kwdef mutable struct MyType
    a::Int = 1
    b::MyType2 = MyType2(3.0)

    MyType(a,b) = a > 10 ? error("foo") : new(a,b)
end

StructTypes.StructType(::Type{MyType}) = StructTypes.Mutable()
StructTypes.StructType(::Type{MyType2}) = StructTypes.Mutable()

json_string = """{"a":11, "b":{"c":-3.0}}"""
hello_world = JSON3.read(json_string, MyType; parsequoted= true)

```

Dear community,  
here is the ref to the issue i opened:  
[StructTypes.Mutable() does not call the inner constructor · Issue #266 · quinnj/JSON3.jl · GitHub](https://github.com/quinnj/JSON3.jl/issues/266)

Lets assume i have a two mutable structs of MyType2 which is then nested into MyType.  
I expect JSON3.read to trow an error since json\_string has to invalid input according to inner constructor. My understand is that StuctTypes.Muable() calls an emtpy constructor and then mutates the arguements in order to deal with missing arguments in the json\_string. I want it to be able top handly missing arguments but still call the innerconstructor to validate the input.  
Is there a way to throw theses errors using StructTypes while parsing the “json\_string”.  
Due to legacy reasons i don’t want to change the structs to be immutable.  
Tips are very appreciated, thank you for your support in advance.

best regards  
BlondDon

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [July 26, 2023, 1:58pm UTC](https://discourse.julialang.org/t/structtypes-mutable-does-not-call-the-inner-constructor/102118/2 "2023-07-26T13:58:04Z")

</div>

_Disclaimer_: not a JSON3 contributor/maintainer here.

The issue raised by you has less to do with `JSON3` or `StructTypes` packages - it touches the more general discussion of serialization/deserialization, with a particular focus on `mutable` data types.

Technically speaking, you can always do this:

```julia
using JSON3
using StructTypes

@kwdef mutable struct MyType2
    c::Float64 = 3.0

    MyType2(c) = c < 0.0 ? error("bar") : new(c)
end

StructTypes.StructType(::Type{MyType}) = StructTypes.Mutable()

Base.@kwdef mutable struct MyType
    a::Int = 1
    b::MyType2 = MyType2(3.0)

    MyType(a, b) = a > 10 ? error("foo") : new(a, b)
end

StructTypes.StructType(::Type{MyType2}) = StructTypes.Mutable()

v = MyType(9, MyType2(3.0))

v.a = 11
v.b.c = -3.0

serialized = JSON3.write(v)
deserialized = JSON3.read(serialized, MyType; parsequoted=true)

@info v
@info deserialized

```

Now, `v` exists in your session in a state not achievable by the direct usage of the constructor. And that is totally legitimate for a `mutable struct`: you can have restrictions in the constructor but mutate your `struct` fields at a later time (imagine the scenario where you initiate a “person” at age 0 - but increase the mutable field `age` as the person grows older).

You might what to serialize the object `v` and deserialize it later.

The deserialization has nothing to do with constructing a new object (via `new()`) - the deserialization ensures that **a previous state of your object will be restored** : that is part of the job of `StructTypes` - and subsequently of the `JSON3` package.

Imposing the restrictions from `new()` would directly violate the very meaning of `restoring an object to a previous state`. And would break the package deserialization functionality.

So, when you say,

> [@BlondDon](#):
>
> I expect JSON3.read to trow an error since json\_string has to invalid input according to inner constructor.

you basically say that you expect JSON3 to give up the `deserialization` for mutable structs and do something else.

A solution for your issue might consist in building your own validator and checking the deserialized objects (and even instantiating the objects to the desired values/state if the check fails).

Also, if you don’t want some states to be representable for your `struct` type, you can do something like this:

```julia
@kwdef mutable struct MyType2
    c::Float64 = 3.0

    MyType2(c) = c < 0.0 ? error("bar") : new(c)
end

function Base.setproperty!(v::MyType2, field::Symbol, value)
    field == :c && value < 0.0 && error("bar")
    setfield!(v, field, value)
end

mt2 = MyType2(3.0)

# this will fail now
mt2.c = -3.0

```

Imposing such constraint will at least prevent “invalid” states from being reached in your program - and you’ll never actually end up doing serialization of the “illegal state.”

P. S. In the case of the immutable structs, the premise is that the field values will not change once the struct is instantiated - consequently, you will not be able to serialize some “illegal state” of your struct (thus, there is no question of what is going to happen at the deserialization time).

Now, there can be both custom serialization and deserialization behaviors implemented. In such scenarios can be totally legitimate to impose any restrictions you want (both related to the `new()` constraints or something else - even arbitrary).

---

<div class="post-metadata">

**Author:** ![BlondDon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blonddon/32/51819_2.png) [@BlondDon](https://discourse.julialang.org/u/BlondDon)\
**Post date:** [July 31, 2023, 9:12am UTC](https://discourse.julialang.org/t/structtypes-mutable-does-not-call-the-inner-constructor/102118/3 "2023-07-31T09:12:24Z")

</div>

Hi @algunion

Thanks for your reply. Unfortunately the serialization step happens outside of julia by a client which then sends me the JSON. My goal is to detect the illegal state during deserialization(while constructing the julia object).  
I tried the other route of making all struct immuatable, but now there seems to be issue with the @kwdef since now i have to pass all parameters even if i have a @kwdef in place for everything. In the example below the “a” parameter is not passed in the json, but should default to 1 as the kwdef defines. Is there a way to make it work this way?  
If clients have to pass all parameters, this would be a unnecessary overhead as well as a breaking API change for us.

```julia
using JSON3
using StructTypes

Base.@kwdef struct MyType2
    c::Float64 = 3.0

    MyType2(c) = c < 0.0 ? error("bar") : new(c)
end

Base.@kwdef struct MyType
    a::Int = 1
    b::MyType2 = MyType2(3.0)

    MyType(a,b) = a > 10 ? error("foo") : new(a,b)
end

StructTypes.StructType(::Type{MyType}) = StructTypes.Struct()
StructTypes.StructType(::Type{MyType2}) = StructTypes.Struct()

json_string = """{"b":{"c":3.0}}"""
hello_world = JSON3.read(json_string, MyType; parsequoted= true)

```

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [July 31, 2023, 11:01am UTC](https://discourse.julialang.org/t/structtypes-mutable-does-not-call-the-inner-constructor/102118/4 "2023-07-31T11:01:59Z")

</div>

> [@BlondDon](#):
>
> I tried the other route of making all struct immuatable, but now there seems to be issue with the @kwdef since now i have to pass all parameters even if i have a @kwdef in place for everything.

You’ll have to deal with the “missing” fields. I hope this helps:

```julia
using JSON3
using StructTypes

Base.@kwdef struct MyType2
    c::Float64 = 3.0

    MyType2(c) = c < 0.0 ? error("bar") : new(c)
end

Base.@kwdef struct MyType
    a::Int = 1
    b::MyType2 = MyType2(3.0)

    function MyType(a, b)
        !isnothing(a) && a > 10 && error("foo")
        isnothing(a) && return MyType(b=b)
        isnothing(b) && return MyType(a=a)
        new(a, b)
    end
end

StructTypes.StructType(::Type{MyType}) = StructTypes.Struct()
StructTypes.StructType(::Type{MyType2}) = StructTypes.Struct()

json_string = """{"b":{"c":3.0}}"""
JSON3.read(json_string, MyType; parsequoted=true)

json_string = """{"a": 2, "b":{"c":3.0}}"""
JSON3.read(json_string, MyType; parsequoted=true)

json_string = """{"a": 2}"""
JSON3.read(json_string, MyType; parsequoted=true)

```

The _problem_ is that `JSON3` doesn’t call your `keyword`-based constructors (defined via `@kwdef`). So you must do that yourself (see the adjusted code I pasted above).
