### ----------------------------------------------------
### -- JSON::Class
### -- Licenses: Artistic-2.0
### -- Authors: Vadim Belman
### -- File: docs/md/JSON/Class.md
### ----------------------------------------------------
### -- Chunk 2 of 2
deserialized{$key};
        say "Deserialized after accessing? ", $deserialized{$key}:has;
    }
    ```
    
    Sample output:
    
    ``` 
    --- Initial        : {"plain":"string","i2":{"section":"D","chapter":"66.6"},"i1":{"id":"AB12-EFZK","quantity":2}}
    --- Delete i1      : Item1.new(id => "AB12-EFZK", quantity => 2)
    --- Default is     : <not there>
    --- JSON without i1: {"i2":{"section":"D","chapter":"66.6"},"plain":"string"}
    
    --- Key 'in' ---
    Deserialized before accessing? False
    ... deserializing key 'in'
    Value: Item2.new(section => "DS", chapter => "43.2.1")
    Deserialized after accessing? True
    
    --- Key 'sval' ---
    Deserialized before accessing? False
    ... deserializing key 'sval'
    Value: some string
    Deserialized after accessing? True
    
    --- Key 'entry1' ---
    Deserialized before accessing? False
    ... deserializing key 'entry1'
    Value: Item1.new(id => "FROM-JSON", quantity => 0)
    Deserialized after accessing? True
    ```

## Array Attributes

  - From [examples/typed-array.raku](../../../examples/typed-array.raku)
    
    ``` raku
    class Rec {
        has Str:D $.description is required;
    }
    
    class Struct is json(:implicit) {
        has Array[Real:D](Array:D) @.matrix is json(:predicate);
        has Rec:D %.rec is json(:name<records>, :predicate);
    }
    
    my $struct =
        Struct.new:
            matrix => [
                [1, 2],
                [pi, e],
                [42.12, 13.666, 4321],
            ],
            rec => {
                R1 => Rec.new(:description("test description")),
                R2 => Rec.new(:description("test description 2")),
            };
    
    say "=== Serializing ===";
    say $struct.to-json(:pretty, :sorted-keys);
    
    say "=== Deserializing ===";
    my $deserialized =
        Struct.from-json:
            q<{"matrix":[[3,4,5],[3.141592653589793e0,2.718281828459045e0],[42.0,32.16,99]],
               "records":{"R1":{"description":"for the first"},"R2":{"description":"for the second"}}}>;
    
    say "Has .matrix: ", $deserialized.json-has-matrix;
    say "Has .rec   : ", $deserialized.json-has-records; # JSON key name is used for method name
    say "Matrix     : ", $deserialized.matrix;
    say "Has .matrix: ", $deserialized.json-has-matrix;
    say "Has .rec   : ", $deserialized.json-has-records;
    say "Records    : ", $deserialized.rec;
    say "Has .matrix: ", $deserialized.json-has-matrix;
    say "Has .rec   : ", $deserialized.json-has-records;
    ```
    
    Sample output:
    
    ``` 
    === Serializing ===
    {
      "matrix": [
        [
          1,
          2
        ],
        [
          3.141592653589793e0,
          2.718281828459045e0
        ],
        [
          42.12,
          13.666,
          4321
        ]
      ],
      "records": {
        "R1": {
          "description": "test description"
        },
        "R2": {
          "description": "test description 2"
        }
      }
    }
    === Deserializing ===
    Has .matrix: False
    Has .rec   : False
    Matrix     : [[3 4 5] [3.141592653589793 2.718281828459045] [42 32.16 99]]
    Has .matrix: True
    Has .rec   : False
    Records    : {R1 => Rec.new(description => "for the first"), R2 => Rec.new(description => "for the second")}
    Has .matrix: True
    Has .rec   : True
    ```

## Using Configuration And Type Mapping

These two subjects are tightly bound to each other, hence single example for both subjects.

  - From [examples/config-and-type-map.raku](../../../examples/config-and-type-map.raku)
    
    ``` raku
    # To set global defaults .global to be invoked as early as possible or otherwise the global config can be vivified
    # somewhere else with standard defaults.
    JSON::Class::Config.global: :pretty, :sorted-keys, :!skip-null;
    
    class Record is json {
        has Int $.count;
        has Str $.what;
    }
    
    class Status {
        has Str $.code;
        has Bool $.verified;
        has Str $.notes;
    }
    
    # Indicate that this class wants to replace Record whenever possible.
    class RecWrapper is json-wrap(Record) {
        has Bool $.available is mooish(:lazy);
    
        method build-available {
            $.count > 0;
        }
    }
    
    class StatusTools is Status {
        # Just a mockup of what could be used for accessing some API, for example.
        method update-record {
            "will-update" => $.code
        }
    }
    
    JSON::Class::Config.map-types: RecWrapper, (Status) => StatusTools;
    
    class Foo is json {
        has Record:D $.record is required is json(:name<rec>);
        has Status:D $.status is required is json(:name<st>);
    }
    
    say "--- Using global config with type maps";
    my $foo =
        Foo.from-json:
            q<{"rec":{"count":0,"what":"irrelevant"},"st":{"code":"1A-CD3","verified":false,"notes":"to be done"}}>;
    
    say ".record attribute type: ", $foo.record.^name;
    say ".status attribute type: ", $foo.status.^name;
    
    $foo.status.update-record;
    
    say "Serialization of mapped types:\n", $foo.to-json.indent(2);
    
    say "--- Using custom config with no type maps";
    my $config = JSON::Class::Config.new;
    
    $foo = Foo.from-json:
                q<{"rec":{"count":0,"what":"irrelevant"},"st":{"code":"1A-CD3","verified":false,"notes":"to be done"}}>,
                :$config;
    
    say ".record attribute type: ", $foo.record.^name;
    say ".status attribute type: ", $foo.status.^name;
    
    try { $foo.status.update-record };
    say "We expect an exception here: ", $!.^name;
    say "Exception message: ", $!.message;
    
    say "Serialization with custom config using standard defaults:\n", $foo.to-json(:$config).indent(2);
    ```
    
    Sample output. Notice how different configuration defaults affect serialization output without passing any arguments to the `to-json` method:
    
    ``` 
    --- Using global config with type maps
    .record attribute type: RecWrapper
    .status attribute type: StatusTools
    Serialization of mapped types:
      {
        "rec": {
          "count": 0,
          "what": "irrelevant"
        },
        "st": {
          "code": "1A-CD3",
          "notes": "to be done",
          "verified": false
        }
      }
    --- Using custom config with no type maps
    .record attribute type: Record
    .status attribute type: Status
    We expect an exception here: X::Method::NotFound
    Exception message: No such method 'update-record' for invocant of type 'Status'
    Serialization with custom config using standard defaults:
      {"st":{"notes":"to be done","verified":false,"code":"1A-CD3"},"rec":{"what":"irrelevant","count":0}}
    ```

## Custom Serialziers And Deserializers

There are more than we can fit into this example. We can have individual custom marshalling for hash and array values, and hash keys too. We can specify marshallers for sequence elements too.

  - From [examples/custom-marshallers.raku](../../../examples/custom-marshallers.raku)
    
    ``` raku
    class Record is json {
        has Str $.description is json( :serializer({ "pfx:" ~ $_ }),    # :to-json alias can be used instead
                                       :deserializer({ .substr(4) }) ); # :from-json can be used instead
    }
    
    class Article is json {
        has Str $.title;
    
        method serialize {
            %{ "-ttl-" => $!title }
        }
        method deserialize(%p) {
            die "bad profile" unless %p<-ttl->;
            self.new: title => %p<-ttl->;
        }
    }
    
    class Foo is json {
        has Record:D $.rec is required;
        has Article:D $.article is required;
    }
    
    my $config = JSON::Class::Config.new(:pretty, :sorted-keys);
    $config.set-helpers: Article, serializer => 'serialize', deserializer => 'deserialize';
    
    my $foo = Foo.new:
                rec => Record.new(:description("means nothing")),
                article => Article.new(:title("The Deep Thought: Complexity Of The Answer"));
    
    say "Custom serializers affect JSON:\n", $foo.to-json(:$config).indent(2);
    # "article" key is affected, but "rec" is using attribute's marshallizers.
    say "Serializing with default config:\n", $foo.to-json(:pretty, :sorted-keys).indent(2);
    
    $foo = Foo.from-json:
            q<{"rec":{"description":"pfx:stuff"},"article":{"-ttl-":"The Deep Thought: Gimme A Bit More Time"}}>,
            :$config;
    
    say "Custom deserializers handle 'weird' JSON:\n", $foo.raku.indent(2);
    ```
    
    Sample output:
    
    ``` 
    Custom serializers affect JSON:
      {
        "article": {
          "-ttl-": "The Deep Thought: Complexity Of The Answer"
        },
        "rec": {
          "description": "pfx:means nothing"
        }
      }
    Serializing with default config:
      {
        "article": {
          "title": "The Deep Thought: Complexity Of The Answer"
        },
        "rec": {
          "description": "pfx:means nothing"
        }
      }
    Custom deserializers handle 'weird' JSON:
      Foo.new(rec => Record.new(description => "stuff"), article => Article.new(title => "The Deep Thought: Gimme A Bit More Time"))
    ```

## Skipping Custom Marshalling

Here we use custom marshallers too, this time for array values. But there is a trick: serializer and deserializer do not modify every third value. To do so they call `json-I-cant` routine giving up the task to `JSON::Class`

  - From [examples/skipping-marshalling.raku](../../../examples/skipping-marshalling.raku)
    
    ``` raku
    my $skiped-serializations = 0;
    my $skipped-deserializations = 0;
    
    class Foo is json {
        has Int:D @.counts is json(
            :serializer(
                value => -> $v {
                    if $v % 3 == 0 {
                        ++$skiped-serializations;
                        json-I-cant
                    }
                    $v * 1000
                } ),
            :deserializer(
                value => -> $v {
                    if $v < 1000 {
                        ++$skipped-deserializations;
                        json-I-cant
                    }
                    $v div 1000
                } ));
    }
    
    my $foo = Foo.new(counts => ^22);
    
    my $json = $foo.to-json;
    
    say $json;
    say Foo.from-json($json);
    say "I skipped ", $skiped-serializations, " serializations and ", $skipped-deserializations, " deserializations";
    ```
    
    Sample output:
    
    ``` 
    {"counts":[0,1000,2000,3,4000,5000,6,7000,8000,9,10000,11000,12,13000,14000,15,16000,17000,18,19000,20000,21]}
    Foo.new(counts => Array[Int:D].new(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21))
    I skipped 8 serializations and 8 deserializations
    ```

# METHODS

## JSONified Class

  - **`method to-json(:$config, Bool :$raw, Bool :$pretty, Bool :$sorted-keys, Bool :$enums-as-value, UInt :$spacing)`**
    
    Serialize an instance of JSON class. Arguments:
    
      - [`JSON::Class::Config:D`](Class/Config.md) **`:$config`**, **`:%config`**
        
        Either a configuration object or a profile hash which will be used to create one.
    
      - `Bool` **`:$raw`**
        
        If *True* then the method will return raw serialization of the object, i.e. a [`Hash`](https://docs.raku.org/type/Hash).
    
      - `Bool` **`:$pretty`**, `Bool` **`:$sorted-keys`**, `Bool` **`:$enums-as-value`**, `UInt` **`:$spacing`**
        
        Same as [`JSON::Fast`](https://raku.land/cpan:TIMOTIMO/JSON::Fast) `to-json` arguments.

  - **`proto method from-json(|)`**
    
      - **`method from-json(Str:D $json, :$config, Bool :$allow-jsonc, Bool :$enums-as-value)`**
    
      - **`method from-json(%json, :$config, Bool :$allow-jsonc, Bool :$enums-as-value)`**
    
      - **`method from-json(@json, :$config, Bool :$allow-jsonc, Bool :$enums-as-value)`**
    
    Deserialize JSON class from either a JSON string, or raw hash or array.
    
      - `Str:d` **`$json`**
        
        JSON source
    
      - **`%json`**, **`@json`**
        
        Deserialize from pre-parsed JSON data. A hash is only making sense for a JSON class. An array can be fed to both JSON class and JSON sequence; each will treat it differently.
    
      - `JSON::Class::Config:D` **`:$config`**, **`:%config`**
        
        Either a configuration object or a profile hash which will be used to create one.
    
      - `Bool` **`:$allow-jsonc`**, `Bool` **`:$enums-as-value`**
        
        Same as [`JSON::Fast`](https://raku.land/cpan:TIMOTIMO/JSON::Fast) `from-json` arguments.

  - **`method json-serialize(JSON::Class::Config :$config)`**
    
    Implements the actual serialization task.

  - **`method json-deserialize($from, JSON::Class::Config:D :$config)`**
    
    Implements the actual deserialization task. `$from` can be a hash or an array. JSON sequences only accept arrays.

  - **`proto method json-serialize-value(|)`**
    
    Produce a value that can be immediately serialized by the underlying JSON module (think [`JSON::Fast`](https://raku.land/?q=JSON::Fast)). Say, for complex objects it would produce a hash; for an [`Enumeration`](https://docs.raku.org/type/Enumeration) it would return either its symbol name, or value, depending on [`JSON::Class::Config`](Class/Config.md) `enums-as-value` parameter.
    
    This is a low-level method also used as a fallback when other means of serialization are not possible for whatever reason.
    
      - **`multi method json-serialize-value(Mu \value, *%nameds)`**
        
        Tries to serialize any kind of `value`. Any of `%nameds` are passed down the call chain to other candidates.
    
      - **`multi method json-serialize-value(Mu \target-type, Mu \value, JSON::Class::Config :$config, *%nameds)`**
        
        There are a few candidates with two-argument signature, responsible for serializing different kinds of `target-type`. These are somewhat specialized to serve `JSON::Class` internals. Yet, it is possible to introduce own candidates in a subclass if necessary.
        
        Some candidates are using the `$config` named argument. Any unknown arguments are passed down the call chain to other candidates.

  - **`proto method json-deserialize-value(Mu \dest-type, Mu \json-value, JSON::Class::Config :$config)`**
    
    This is a low-level method to produce a `dest-type` object from a `json-value` which is one of [a JSON data type](https://en.wikipedia.org/wiki/JSON#Data_types), returned by `from-json` routine.

  - **`method json-create(|)`**
    
    This method acts almost like `new`, but the difference is in what class it eventually creates. If it belongs to a run-time JSONified class, i.e. to one created by [`JSON::Class::Config`](Class/Config.md) `jsonify` method, then `json-create` would return an instance of the original class, not its JSONification.
    
    Normally one doesn't need to use this method. But if it comes down to manually instantiating JSON classes then it would be preferred over the standard constructor.

  - **`method clone-from(Mu $obj, *%twiddles)`**
    
    This method allows to create an instance of its class using attribute values of `$obj`. The point is that it makes it possible to have a copy of type different from that of `$obj`. `JSON::Class` uses it to JSONify instances of non-JSON classes, for example.
    
    *Note* that this is one of few methods whose names don't start with `json-` prefix.

  - **`method json-all-set()`**
    
    Returns *True* if all postponed JSON data have been lazily deserialized.

  - **`method json-config-class()`**
    
    Returns the default configuration class type object. Overridable.

  - **`method json-config()`**
    
    Get the currently active configuration object if invoked within an active configuration context. Otherwise creates a new configuration context using class' defaults and returns its config.

  - **`method json-config-context(&code, :$config, *%twiddles)`**
    
    Creates a new configuration context by pulling in defaults for the config from various sources. Invokes user `&code` with `$*JSON-CLASS-CONFIG` context variable set with a configuration object. [`JSON::Class::Details`](Class/Details.md) has some more information on how configuration context gets built.

# SEE ALSO

  - [`JSON::Class::Details`](Class/Details.md)

  - [`JSON::Class::Config`](Class/Config.md)

  - [`JSON::Class::Descriptor`](Class/Descriptor.md)

  - [`JSON::Class::Object`](Class/Object.md)

  - [`JSON::Class::Dict`](Class/Dict.md)

  - [`JSON::Class::Sequence`](Class/Sequence.md)

  - [`JSON::Class::Types`](Class/Types.md)

  - [`Changes`](../../../ChangeLog.md)

  - [`INDEX`](../../../INDEX.md)

# COPYRIGHT

(c) 2023, Vadim Belman <vrurg@cpan.org>

# LICENSE

Artistic License 2.0

See the [*LICENSE*](../../../LICENSE) file in this distributio


