<p dir="auto"><strong>Description</strong><br>
The documentation for <code class="notranslate">ObjectNormalizer</code> states that "It supports calling the constructor during the denormalization process." but that is not true when one of the constructor arguments is an array of objects. Currently (since <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="171627430" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/19649" data-hovercard-type="pull_request" data-hovercard-url="/symfony/symfony/pull/19649/hovercard" href="https://github.com/symfony/symfony/pull/19649">#19649</a>), is is only possible to denormalize an "array of objects" property  if that property has a setter method or is public.</p>
<p dir="auto"><strong>Example</strong></p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#!/usr/bin/env php
&lt;?php

namespace App;

use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

require __DIR__ . '/vendor/autoload.php';

class Item
{
    /**
     * @var string
     */
    private $id;

    /**
     * Item constructor.
     * @param string $id
     */
    public function __construct(string $id)
    {
        $this-&gt;id = $id;
    }

    public function getId(): string
    {
        return $this-&gt;id;
    }
}

class Pack
{
    /**
     * @var Item[]
     */
    private $items;

    /**
     * Pack constructor.
     * @param Item[] $items
     */
    public function __construct(array $items)
    {
        $this-&gt;items = $items;
    }

    public function getItems(): array
    {
        return $this-&gt;items;
    }
}

$phpDocExtractor = new PhpDocExtractor();
$normalizer = new ObjectNormalizer(null, null, null, $phpDocExtractor);
$serializer = new Serializer([$normalizer, new ArrayDenormalizer()]);

$itemData = [&quot;id&quot; =&gt; &quot;1&quot;];
$listData = ['items' =&gt; [$itemData]];

dump($serializer-&gt;denormalize($listData, Pack::class));"><pre class="notranslate"><span class="pl-c">#!/usr/bin/env php</span>
<span class="pl-ent">&lt;?php</span>

<span class="pl-k">namespace</span> <span class="pl-v">App</span>;

<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">PropertyInfo</span>\<span class="pl-v">Extractor</span>\<span class="pl-v">PhpDocExtractor</span>;
<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">Serializer</span>\<span class="pl-v">Normalizer</span>\<span class="pl-v">ArrayDenormalizer</span>;
<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">Serializer</span>\<span class="pl-v">Normalizer</span>\<span class="pl-v">ObjectNormalizer</span>;
<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">Serializer</span>\<span class="pl-v">Serializer</span>;

<span class="pl-k">require</span> __DIR__ . <span class="pl-s">'/vendor/autoload.php'</span>;

<span class="pl-k">class</span> <span class="pl-v">Item</span>
{
    <span class="pl-c">/**</span>
<span class="pl-c">     * @var string</span>
<span class="pl-c">     */</span>
    <span class="pl-k">private</span> <span class="pl-c1"><span class="pl-c1">$</span>id</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * Item constructor.</span>
<span class="pl-c">     * @param string $id</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">__construct</span>(<span class="pl-smi">string</span> <span class="pl-s1"><span class="pl-c1">$</span>id</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">id</span> = <span class="pl-s1"><span class="pl-c1">$</span>id</span>;
    }

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getId</span>(): <span class="pl-smi">string</span>
    {
        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">id</span>;
    }
}

<span class="pl-k">class</span> <span class="pl-v">Pack</span>
{
    <span class="pl-c">/**</span>
<span class="pl-c">     * @var Item[]</span>
<span class="pl-c">     */</span>
    <span class="pl-k">private</span> <span class="pl-c1"><span class="pl-c1">$</span>items</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * Pack constructor.</span>
<span class="pl-c">     * @param Item[] $items</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">__construct</span>(<span class="pl-smi">array</span> <span class="pl-s1"><span class="pl-c1">$</span>items</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">items</span> = <span class="pl-s1"><span class="pl-c1">$</span>items</span>;
    }

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getItems</span>(): <span class="pl-smi">array</span>
    {
        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">items</span>;
    }
}

<span class="pl-s1"><span class="pl-c1">$</span>phpDocExtractor</span> = <span class="pl-k">new</span> <span class="pl-v">PhpDocExtractor</span>();
<span class="pl-s1"><span class="pl-c1">$</span>normalizer</span> = <span class="pl-k">new</span> <span class="pl-v">ObjectNormalizer</span>(<span class="pl-c1">null</span>, <span class="pl-c1">null</span>, <span class="pl-c1">null</span>, <span class="pl-s1"><span class="pl-c1">$</span>phpDocExtractor</span>);
<span class="pl-s1"><span class="pl-c1">$</span>serializer</span> = <span class="pl-k">new</span> <span class="pl-v">Serializer</span>([<span class="pl-s1"><span class="pl-c1">$</span>normalizer</span>, <span class="pl-k">new</span> <span class="pl-v">ArrayDenormalizer</span>()]);

<span class="pl-s1"><span class="pl-c1">$</span>itemData</span> = ["<span class="pl-s">id</span>" =&gt; "<span class="pl-s">1</span>"];
<span class="pl-s1"><span class="pl-c1">$</span>listData</span> = [<span class="pl-s">'items'</span> =&gt; [<span class="pl-s1"><span class="pl-c1">$</span>itemData</span>]];

dump(<span class="pl-s1"><span class="pl-c1">$</span>serializer</span>-&gt;<span class="pl-en">denormalize</span>(<span class="pl-s1"><span class="pl-c1">$</span>listData</span>, <span class="pl-v">Pack</span>::class));</pre></div>
<p dir="auto">Executing this script from a project root will print:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="App\Pack {#20
  -items: array:1 [
    0 =&gt; array:1 [
      &quot;id&quot; =&gt; &quot;1&quot;
    ]
  ]
}"><pre class="notranslate"><span class="pl-v">App</span>\<span class="pl-v">Pack</span> {<span class="pl-c">#20</span>
  -items: array:<span class="pl-c1">1</span> [
    <span class="pl-c1">0</span> =&gt; array:<span class="pl-c1">1</span> [
      "<span class="pl-s">id</span>" =&gt; "<span class="pl-s">1</span>"
    ]
  ]
}</pre></div>
<p dir="auto">The expected result is:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="App\Pack {#18
  -items: array:1 [
    0 =&gt; App\Item {#40
      -id: &quot;1&quot;
    }
  ]
}"><pre class="notranslate"><span class="pl-v">App</span>\<span class="pl-v">Pack</span> {<span class="pl-c">#18</span>
  -items: array:<span class="pl-c1">1</span> [
    <span class="pl-c1">0</span> =&gt; <span class="pl-v">App</span>\<span class="pl-v">Item</span> {<span class="pl-c">#40</span>
      -id: "<span class="pl-s">1</span>"
    }
  ]
}</pre></div>
<p dir="auto">That expected result can be obtained by replacing the constructor of <code class="notranslate">Pack</code> with a setter for <code class="notranslate">$items</code>:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#!/usr/bin/env php
&lt;?php

namespace App;

use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;

require __DIR__ . '/vendor/autoload.php';

class Item
{
    /**
     * @var string
     */
    private $id;

    /**
     * Item constructor.
     * @param string $id
     */
    public function __construct(string $id)
    {
        $this-&gt;id = $id;
    }

    public function getId(): string
    {
        return $this-&gt;id;
    }
}

class Pack
{
    /**
     * @var Item[]
     */
    private $items;

    public function setItems(array $items): self
    {
        $this-&gt;items = $items;

        return $this;
    }

    public function getItems(): array
    {
        return $this-&gt;items;
    }
}

$phpDocExtractor = new PhpDocExtractor();
$normalizer = new ObjectNormalizer(null, null, null, $phpDocExtractor);
$serializer = new Serializer([$normalizer, new ArrayDenormalizer()]);

$itemData = [&quot;id&quot; =&gt; &quot;1&quot;];
$listData = ['items' =&gt; [$itemData]];

dump($serializer-&gt;denormalize($listData, Pack::class));"><pre class="notranslate"><span class="pl-c">#!/usr/bin/env php</span>
<span class="pl-ent">&lt;?php</span>

<span class="pl-k">namespace</span> <span class="pl-v">App</span>;

<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">PropertyInfo</span>\<span class="pl-v">Extractor</span>\<span class="pl-v">PhpDocExtractor</span>;
<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">Serializer</span>\<span class="pl-v">Normalizer</span>\<span class="pl-v">ArrayDenormalizer</span>;
<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">Serializer</span>\<span class="pl-v">Normalizer</span>\<span class="pl-v">ObjectNormalizer</span>;
<span class="pl-k">use</span> <span class="pl-v">Symfony</span>\<span class="pl-v">Component</span>\<span class="pl-v">Serializer</span>\<span class="pl-v">Serializer</span>;

<span class="pl-k">require</span> __DIR__ . <span class="pl-s">'/vendor/autoload.php'</span>;

<span class="pl-k">class</span> <span class="pl-v">Item</span>
{
    <span class="pl-c">/**</span>
<span class="pl-c">     * @var string</span>
<span class="pl-c">     */</span>
    <span class="pl-k">private</span> <span class="pl-c1"><span class="pl-c1">$</span>id</span>;

    <span class="pl-c">/**</span>
<span class="pl-c">     * Item constructor.</span>
<span class="pl-c">     * @param string $id</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">__construct</span>(<span class="pl-smi">string</span> <span class="pl-s1"><span class="pl-c1">$</span>id</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">id</span> = <span class="pl-s1"><span class="pl-c1">$</span>id</span>;
    }

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getId</span>(): <span class="pl-smi">string</span>
    {
        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">id</span>;
    }
}

<span class="pl-k">class</span> <span class="pl-v">Pack</span>
{
    <span class="pl-c">/**</span>
<span class="pl-c">     * @var Item[]</span>
<span class="pl-c">     */</span>
    <span class="pl-k">private</span> <span class="pl-c1"><span class="pl-c1">$</span>items</span>;

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">setItems</span>(<span class="pl-smi">array</span> <span class="pl-s1"><span class="pl-c1">$</span>items</span>): <span class="pl-smi"><span class="pl-smi">self</span></span>
    {
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">items</span> = <span class="pl-s1"><span class="pl-c1">$</span>items</span>;

        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>;
    }

    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getItems</span>(): <span class="pl-smi">array</span>
    {
        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">items</span>;
    }
}

<span class="pl-s1"><span class="pl-c1">$</span>phpDocExtractor</span> = <span class="pl-k">new</span> <span class="pl-v">PhpDocExtractor</span>();
<span class="pl-s1"><span class="pl-c1">$</span>normalizer</span> = <span class="pl-k">new</span> <span class="pl-v">ObjectNormalizer</span>(<span class="pl-c1">null</span>, <span class="pl-c1">null</span>, <span class="pl-c1">null</span>, <span class="pl-s1"><span class="pl-c1">$</span>phpDocExtractor</span>);
<span class="pl-s1"><span class="pl-c1">$</span>serializer</span> = <span class="pl-k">new</span> <span class="pl-v">Serializer</span>([<span class="pl-s1"><span class="pl-c1">$</span>normalizer</span>, <span class="pl-k">new</span> <span class="pl-v">ArrayDenormalizer</span>()]);

<span class="pl-s1"><span class="pl-c1">$</span>itemData</span> = ["<span class="pl-s">id</span>" =&gt; "<span class="pl-s">1</span>"];
<span class="pl-s1"><span class="pl-c1">$</span>listData</span> = [<span class="pl-s">'items'</span> =&gt; [<span class="pl-s1"><span class="pl-c1">$</span>itemData</span>]];

dump(<span class="pl-s1"><span class="pl-c1">$</span>serializer</span>-&gt;<span class="pl-en">denormalize</span>(<span class="pl-s1"><span class="pl-c1">$</span>listData</span>, <span class="pl-v">Pack</span>::class));</pre></div>
<p dir="auto">Currently when a property is private and no getter is defined, symfony won't get the value in an object's property, instead an exception will be thrown. However the private property could very well be accessible using the magic <code class="notranslate">__call</code> method. Actually this is very useful to have for Doctrine entities that extend the <code class="notranslate">Doctrine\Common\Persistence\PersistentObject</code> abstract class, which uses the <code class="notranslate">__call</code> method to provide getProperty, setProperty and addProperty functionality.</p>