jsonmapper icon indicating copy to clipboard operation
jsonmapper copied to clipboard

What's the intended NULL value handling?

Open SvenRtbg opened this issue 1 year ago • 1 comments

The default setting is to be strict about NULL values if they are not allowed:

    /**
     * Throw an exception, if null value is found
     * but the type of attribute does not allow nulls.
     *
     * @var bool
     */
    public $bStrictNullTypes = true;

However there is one explicit test case in ArrayTest.php that verifies that NULL inside a JSON array of DateTime is translated to NULL in the final array:

    /**
     * Test for an array of classes "@var ClassName[]" with
     * flat/simple json values (string)
     */
    public function testMapTypedSimpleArray()
    {
        $jm = new JsonMapper();
        $sn = $jm->map(
            json_decode('{"typedSimpleArray":["2014-01-02",null,"2014-05-07"]}'),
            new JsonMapperTest_Array()
        );
        $this->assertIsArray($sn->typedSimpleArray);
        $this->assertCount(3, $sn->typedSimpleArray);
        $this->assertInstanceOf('DateTime', $sn->typedSimpleArray[0]);
        $this->assertNull($sn->typedSimpleArray[1]);
        $this->assertInstanceOf('DateTime', $sn->typedSimpleArray[2]);
        $this->assertSame(
            '2014-01-02', $sn->typedSimpleArray[0]->format('Y-m-d')
        );
        $this->assertSame(
            '2014-05-07', $sn->typedSimpleArray[2]->format('Y-m-d')
        );
    }

Every other test I have seen cares about null values to be either explicitly allowed, or the values are transformed into simple type values that match the required type:

    /**
     * Test for an array of strings - "@var string[]"
     */
    public function testStrArray()
    {
        $jm = new JsonMapper();
        $sn = $jm->map(
            json_decode('{"strArray":["str",false,2.048]}'),
            new JsonMapperTest_Array()
        );
        $this->assertSame(["str", "", "2.048"], $sn->strArray);
    }

One could argue that in the first case, the task is to keep the number of elements, and a side quest is to wrap the strings into DateTime objects if possible, and NULL would not be transferable into a useful DateTime object.

I have not seen another test verifying that non-wrapping objects like JsonMapperTest_Simple are in fact allowing or denying NULL. If I extend the very first test in ArrayTest.php, the added NULL is put into the resulting array just the same:

    /**
     * Test for an array of classes "@var Classname[]"
     */
    public function testMapTypedArray()
    {
        $jm = new JsonMapper();
        $sn = $jm->map(
            json_decode('{"typedArray":[{"str":"stringvalue"},{"fl":"1.2"}, null]}'), # ADDED NULL HERE
            new JsonMapperTest_Array()
        );
        $this->assertIsArray($sn->typedArray);
        $this->assertCount(3, $sn->typedArray); # GET ONE ELEMENT MORE BACK
        $this->assertContainsOnlyInstancesOf(JsonMapperTest_Simple::class, $sn->typedArray); # THAT IS NULL, not the class.
        $this->assertSame('stringvalue', $sn->typedArray[0]->str);
        $this->assertSame(1.2, $sn->typedArray[1]->fl);
    }

It looks like this is likely the reason for the influx in recent "complaints" about array elements being of unexpected type. And I think no matter how we'd fix it, it is a change in behavior that warrants a major version release.

Any insights from anyone?

Referencing #224 here for the same major release.

SvenRtbg avatar Mar 21 '24 18:03 SvenRtbg

From what I recall, arrays don't check bStrictNullTypes, which is why I had a problem. @cweiske previously said here that guarding array elements behind this var would require a new major version. Possibly adding an extra bStrictNullTypesInArrays would allow avoiding a BC break, but it seems clunky.

Array elements are generally not subject to the same level of scrutiny as object properties from my experience - see also #225

dktapps avatar May 15 '24 12:05 dktapps

I'd happily accept a patch that extends the bStrictNullTypes option to null values in array - when a new option is added to get the old behavior back (disabled by default), just as described in #211. That way a new major version would be more secure, while software relying on the old behavior could simply activate the new option when updating JsonMapper.

cweiske avatar May 18 '24 05:05 cweiske

Just to rephrase:

  • bStrictNullTypes defaults to on and will create a breaking behaviour by rejecting array entries that are NULL.
  • A new compatibility flag is added to retain the current behaviour of allowing NULL in arrays.

Let's see if I can get there.

SvenRtbg avatar May 21 '24 09:05 SvenRtbg

Released with v5.0.0.

cweiske avatar Sep 08 '24 10:09 cweiske