Issues

ZF-9416: Zend_Json_Encoder creates invalid JSON for Zend_Json_Decoder

Description

This piece of JSON was created with Zend_Json_Encoder::encode

{"__className":"fgSDK_Types_TTrip","IDuser":1,"Smoker":"flex","NumberPlate":"WI-EN 89","Places":2,"Contactlandline":"0221 54899","Contactmobile":"0175 54899","Description":"ich fahre nach k\u00f6ln","ClientIP":"192.168.1.20","Triptype":"offer","Routings":{"__className":"fgSDK_Types_TRoutingCollection",0:{"__className":"fgSDK_Types_TRouting","Origin":{"__className":"fgSDK_Types_TPlace","Address":"K\u00f6ln, Deutschland","Accuracy":4,"CountryCode":"DE","CountryName":"Deutschland","Longitude":6.9599115,"Latitude":50.9406645,"Placetype":"geo","LatLonBox":{"__className":"fgSDK_Types_TLatLonBox","North":51.0530233,"South":50.8280335,"East":7.2160303,"West":6.7037927}},"Destination":{"__className":"fgSDK_Types_TPlace","Address":"Hamburg, Deutschland","Accuracy":4,"CountryCode":"DE","CountryName":"Deutschland","Longitude":9.9921962,"Latitude":53.5534074,"Placetype":"geo","LatLonBox":{"__className":"fgSDK_Types_TLatLonBox","North":53.6593331,"South":53.4472158,"East":10.248315,"West":9.7360774}},"RoutingIndex":-1}}}

During Decoding using Zend_Json_Decoder::decode this exception is thrown:

Message: Missing key in object encoding: {"__className":"fgSDK_Types_TTrip","IDuser":1,"Smoker":"flex","NumberPlate":"WI-EN 89","Places":2,"Contactlandline":"0221 54899","Contactmobile":"0175 54899","Description":"ich fahre nach köln","ClientIP":"192.168.1.20","Triptype":"offer","Routings":{"__className":"fgSDK_Types_TRoutingCollection",0:{"__className":"fgSDK_Types_TRouting","Origin":{"__className":"fgSDK_Types_TPlace","Address":"Köln, Deutschland","Accuracy":4,"CountryCode":"DE","CountryName":"Deutschland","Longitude":6.9599115,"Latitude":50.9406645,"Placetype":"geo","LatLonBox":{"__className":"fgSDK_Types_TLatLonBox","North":51.0530233,"South":50.8280335,"East":7.2160303,"West":6.7037927}},"Destination":{"__className":"fgSDK_Types_TPlace","Address":"Hamburg, Deutschland","Accuracy":4,"CountryCode":"DE","CountryName":"Deutschland","Longitude":9.9921962,"Latitude":53.5534074,"Placetype":"geo","LatLonBox":{"__className":"fgSDK_Types_TLatLonBox","North":53.6593331,"South":53.4472158,"East":10.248315,"West":9.7360774}},"RoutingIndex":-1}}} Stack trace:

0 D:\server\lib\zend1.10\Zend\Json\Decoder.php(174): Zend_Json_Decoder->_decodeObject()

1 D:\server\lib\zend1.10\Zend\Json\Decoder.php(219): Zend_Json_Decoder->_decodeValue()

2 D:\server\lib\zend1.10\Zend\Json\Decoder.php(174): Zend_Json_Decoder->_decodeObject()

3 D:\server\lib\zend1.10\Zend\Json\Decoder.php(156): Zend_Json_Decoder->_decodeValue()

4 D:\dev\web[REMOVED]\service.[REMOVED]\trunk\application\controllers\TripController.php(38): Zend_Json_Decoder::decode('{"__className":...')

5 D:\server\lib\zend1.10\Zend\Controller\Action.php(513): TripController->putAction()

6 D:\server\lib\zend1.10\Zend\Controller\Dispatcher\Standard.php(289): Zend_Controller_Action->dispatch('putAction')

7 D:\server\lib\zend1.10\Zend\Controller\Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))

8 D:\server\lib\zend1.10\Zend\Application\Bootstrap\Bootstrap.php(97): Zend_Controller_Front->dispatch()

9 D:\server\lib\zend1.10\Zend\Application.php(366): Zend_Application_Bootstrap_Bootstrap->run()

10 D:\dev\web[REMOVED]\service.[REMOVED]\trunk\public\index.php(76): Zend_Application->run()

11 {main}

Comments

Could you please provide the source data (array/object) for debugging?

Please see the attached files.

You need to extract the ZIP file (required types)

in the zend.php you can see the object creation and structure.

Do you need anything else?

br robert

Hi again,

sorry but its the first issue which I'm reported here.

What are the next steps? Is there a quick FIX available?

Can sombody here help in a short term?

br robert

Fixes to issues are prioritized by severity; that said, they're also going to occur based on availability of the contributors/maintainers of the components in question. The easier it is for a contributor to reproduce the issue, the easier it will be for them to work on it.

Is there any chance you can simplify the reproduce case further?

I attached already this 2 Files, including only a few files to reproduce the issue. It should be very very easy to reproduce and debug it.

For my client SOA architecture this functionality is key.

Is there a timeframe which I can communicate anyhow?

br

This bug is related to the fact that one of the classes is an instance of Iterator. In that case the encoder iterates the object itself instead of its variables to produce the JSON string.

It can be reproduced in the following scenarios:


class A implements Iterator {
    
    protected $_stack;
    
    public function __construct(array $array)
    {
        $this->_stack = $array;
    }
    
    public function rewind() {
        reset($this->_stack);
    }

    public function current() {
        return current($this->_stack);
    }

    public function key() {
        return key($this->_stack);
    }

    public function next() {
        return next($this->_stack);
    }

    public function valid() {
        return $this->current() !== false;
    }
}

$a = new A(array("foo"));
$encoded = Zend_Json_Encoder::encode($a);
$decoded = Zend_Json_Decoder::decode($encoded);
// Exception 'Zend_Json_Exception' with message 'Missing key in object encoding: {"__className":"A",0:"foo"}'

class B extends ArrayIterator
{
    
}
$b = new B(array("foo"));
$encoded = Zend_Json_Encoder::encode($b);
$decoded = Zend_Json_Decoder::decode($encoded);
// Exception 'Zend_Json_Exception' with message 'Missing key in object encoding: {"__className":"B",0:"foo"}'

ZF-9416.patch includes a test that demonstrates this bug. The problem relates to the fact that ArrayIterator gets encoded as an object in JSON, which requires a string key for each value. However, when an array without keys is encoded it is an array in JSON, which does not have keys. I'm not sure what the proper behavior should be. Do we convert numeric keys to strings and preserve it as an object? Do we embed an array within the JSON object and follow the same rules for encoding arrays?

I think the proper behavior is demonstrated by json_encode:


$iteratorA = new ArrayIterator(array('foo'));
$encoded = json_encode($iteratorA);
var_dump($encoded);

This produces:


string(11) "{"0":"foo"}" 

I am going to follow this behavior in Zend_Json::encode and add the numeric keys as strings for safety.

In the sample code, it is calling Json_Encoder and Json_Decoder directly. A better approach is to use Json::encode and Json::decode. This way, if you have json support in PHP it will use the native functions json_encode and json_decode. Not only is it faster, but this bug does not occur. The bug only exists in the internal class Json_Encoder (which I will soon fix).

ZF-9416-2.patch fixes the bug and includes a better unit test. I fixed the bug by using the internal _encodeString function for keys within an object, since JSON requires that object keys always be strings. This handles the encoding of numeric keys in the same way as the native json_encode feature now. Unit test now passes. Should be ready to commit.

Unit test written to demonstrate the problem and prove that the patch fixes it. Please use the ZF-9416-2.patch file.

Was not resolved in code (yet).

Resolved in r22452 in trunk and in r22453 in release branch 1.10 with patch "-2" from above.