A completely fair and balanced comparison of php json libraries

Disclaimer: I'm biased

The release of php 5.2.0, which includes by default an extension for converting php values from and to the JSON format, was a good occasion for comparing the different existing php libraries which aim to provide the same capabilities.

This page is an update of the original comparison, made to reflect the improvements that have been made by the various libraries.


The JSON homepage lists quite a few php libraries, but only 4 are in fact available for download and look stable and maintained:

The first one is the only library written in C. Its evident advantage in terms of execution speed is (was) accompanied by portability problems that the other three, being written purely in php, do not have.
PLEASE NOTE that the package available on pecl is slightly different from the code that is part of the php 5.2.1 core distribution, which has seen a couple of bug fixes. Some of those changes have been regarded as controversial by part of the php community (see this blog entry for a detailed description)

The second one was (to the best of my knowledge) the first implementation of JSON in PHP. Its inclusion in PEAR is a guarantee of stability and quality of code.

The third one is more recent, but it is backed up by the company behind the PHP engine, and is part of a framework of components that is evolving at a fast pace and will probably see widespread adoption in the future.

The fourth one was built to add support for the json-rpc protocol to an existing xmlrpc implementation, and later expanded to better handle plain json. The json parsing code was originally taken from the Pear lib and incrementally improved.

Since the original test, a new php library has come to my attention, implementing json encoding and decoding. It is part of the Solar php framework, and it is the third based on the original code from M. Migursky. It implements the same api as the php native extension, ans is contained within a single file. It has not been included in the test matrix for coherence with the previous tests, but might make it into future updates...


The comparison revolves around 4 sections: features matrix, code examples, execution speed and encoding/decoding test results.

All comparisons have been carried out on the most recent code available as of the 20th of April 2007 (read: CVS or unstable versions used where available)

Feature matrix

This list is quite subjective both on the choice of features looked for and in the evaluation of their implementation. I will gladly accept any correction.

FeaturePHP JsonPear JsonZend jsonXmlRpc json
Version 1.2.1 2006/03/311.31 2006/06/2820070420-4556CVS 1.31 2007/03/10
PHP version compatibility 4 (for windows the dll for php 4.3 and above is availabale on pecl4win), 54.0.6, 554.2, 5
PHP files used 0162
Encodes to 7-bit ASCII clean JSON strings, using unicode escape sequences YesYesNoYes
Supports encoding php strings from different charsets Assumes UTF-8Assumes UTF-8Does not convert string data: it is up to the user to take care about conversion to the desired charsetUTF-8, ISO-8859-1, ASCII
Supports decoding JSON data that has not an an array or object as top level element Yes (php 5.2.1), No (pecl 1.2.1)YesYesYes
Supports decoding JSON objects as either php arrays or objects YesYesYesYes
Supports distinguishing a failed decoding from decoding a single NULL value Yes (single NULL value cannot be decoded) up to php 5.2.0 onlyNoYes (exception thrown)Yes
Supports decoding JSON to different charsets Only UTF-8UTF-8, ISO-8859-1, ASCII
Supports features of Javascript outside the scope of JSON None: only data in accord with the Json spec is acceptedParses almost everything as valid (within an array)SomeTries to mimic the behaviour of common web browsers js engines;
keeps type information for float and integer php values (a float value of 1.0 will be encoded ad later decoded by the lib as float, even though in js only 'numbers' exists)
Extra features Implements check to avoid infinite loop on recursive objectsImplements an optional (and somewhat problematic1) check to avoid infinite loop on recursive objects; if the json extensions is enabled, transparently takes advantage of it for faster operation2; has a slightly "twisted" way of encoding php objects3; uses Reflection to encode class definitions, tooProvides a compatibility layer that implement the same API as the PHP native json extension;
adds support for the json-rpc webservice dialect
Changes since last comparison Changed to decode any json values as root elements, not just objects or arrays; added check to avoid infinite loop on recursive objectsnoneadded optional check to avoid infinite loop on recursive objectsfixes in decoding with application of charset transcoding

Code samples

GoalPHP JsonPear JsonZend jsonXmlRpc json
Encode a php value to JSON string $out = json_encode($data); $value = new Services_JSON(); $out = $value->encode($data); $out = Zend_Json::encode($data); $value = php_jsonrpc_encode($data); $out = $value->serialize();
Decode a JSON string to PHP value (Json objects as php objects) $out = json_decode($data); $value = new Services_JSON(); $out = $value->decode($data); try { $out = Zend_Json::decode($data, Zend_Json::TYPE_OBJECT); } catch (Exception $e) { } $value = php_jsonrpc_decode_json($data); if ($value) $out = php_jsonrpc_decode($value, array('decode_php_objs'));
Decode a JSON string to PHP value (Json objects as php arrays) $out = json_decode($data, true); $value = new Services_JSON(SERVICES_JSON_LOOSE_TYPE); $out = $value->decode($data); try { $out = Zend_Json::decode(); } catch (Exception $e) { } $value = php_jsonrpc_decode_json($data); if ($value) $out = php_jsonrpc_decode($value);

Speed comparison results

As with any benchmark results, take these with a grain of salt. No guarantee or claim of correctness is made.

A biggish php array was encoded then decoded 10 times in a row with each lib. Test results are in seconds, smaller is better.

The tests have been carried out on a Win XP machine, with PHP 4.4.5, 5.1.6 and 5.2.1 running in CLI mode and a 'cleaned up' php.ini (and yes, I know most PHP applications are deployed on Linux, but that's what I have available at the moment. YMMV)

The XmlRpc lib appears twice because it can be used in two modes of operation: fully automated conversion or 'manual' conversion, where the user writes code to operate on the single jsonrpcval objects (see lib docs for more details).

PHP 4.4.5
TestPHP JsonPear JsonZend jsonXmlRpc (a)XmlRpc (m)
Data encoding 0,008680,30408NV0,778470,71456
Data decoding 0,013852,99901NV2,050821,91570
PHP 5.1.6
TestPHP JsonPear JsonZend jsonXmlRpc (a)XmlRpc (m)
Data encoding 0,007830,233190,181470,667850,64120
Data decoding 0,014052,120480,694031,495451,41327
PHP 5.2.1
TestPHP JsonPear JsonZend jsonXmlRpc (a)XmlRpc (m)
Data encoding 0,005380,247200,216440,632040,57241
Data decoding 0,010712,189100,813581,481121,21955

An interesting result is PHP 5.2 seems to be marginally slower than 5.1 on all libs except the xmlrpc one, despite the claims made by the authors of it being much more optimized than its predecessors. Did I set it up wrong?
OTOH both versions of php 5 show a clear gain over the aging php 4.

This is the data set used for testing, in PHP source format:

$data1 = array(1, 1.01, 'hello world', true, null, -1, 11.011, '~!@#$%^&*()_+|', false, null); $data2 = array('zero' => $data1, 'one' => $data1, 'two' => $data1, 'three' => $data1, 'four' => $data1, 'five' => $data1, 'six' => $data1, 'seven' => $data1, 'eight' => $data1, 'nine' => $data1); $data = array($data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2);

Encoding / decoding tests results

This last table is especially interesting, showing the different approach that the libraries have taken with regards to the JSON-vs-Javascript compatibility issue. In short: JSON is a subset of Javascript, and while every valid json string is valid Javascript, the reverse is not necessarily true. Every library I tested decodes correctly the json array defined as ["hello","world"], but not all of them accept as valid the string ['hello','world'], which is not valid Json but is valid javascript.

String decoding tests

 Value4XmlRpc decodedJS dec.Native dec.Pear dec.Zend dec.
0string(0) "" NULL
'Invalid data (empty string?)'
NULL NULL NULL
1string(1) "1" int(1) int(1) int(1) int(1)
2string(4) "true" bool(true) bool(true) bool(true) bool(true)
3string(4) "null" NULL NULL NULL NULL
4string(7) ""hello"" string(5) "hello" string(5) "hello" string(5) "hello" string(5) "hello"
5string(11) "not a value" NULL
'Invalid data'
string(11) "not a value" NULL NULL
'Illegal Token'
6string(2) "[]" array(0) { } array(0) { } array(0) { } array(0) { }
7string(3) "[1]" array(1) { [0]=> int(1) } array(1) { [0]=> int(1) } array(1) { [0]=> int(1) } array(1) { [0]=> int(1) }
8string(5) "[1.1]" array(1) { [0]=> float(1.1) } array(1) { [0]=> float(1.1) } array(1) { [0]=> float(1.1) } array(1) { [0]=> float(1.1) }
9string(7) "[-1E+4]" array(1) { [0]=> float(-10000) } array(1) { [0]=> float(-10000) } array(1) { [0]=> float(-10000) } array(1) { [0]=> float(-10000) }
10string(10) "[100.0e-2]" array(1) { [0]=> float(1) } array(1) { [0]=> float(1) } array(1) { [0]=> float(1) } array(1) { [0]=> float(1) }
11string(4) "[.5]" NULL
'Invalid data'
NULL array(1) { [0]=> float(0.5) } array(1) { [0]=> float(0.5) }
12string(4) "[5.]" array(1) { [0]=> float(5) } array(1) { [0]=> float(5) } array(1) { [0]=> int(5) } array(1) { [0]=> int(5) }
13string(3) "[.]" NULL
'Invalid data'
NULL array(1) { [0]=> NULL } NULL
'Illegal number format: .'
14string(6) "[5..5]" NULL
'Invalid data'
NULL array(1) { [0]=> NULL } NULL
'Missing "," in array encoding: [5..5]'
15string(5) "[10e]" NULL
'Invalid data'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
16string(5) "[e10]" NULL
'Invalid data'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
17string(7) "[010e2]" NULL
'Invalid data'
NULL array(1) { [0]=> float(1000) } array(1) { [0]=> float(1000) }
18string(7) "[010.2]" NULL
'Invalid data'
NULL array(1) { [0]=> float(10.2) } array(1) { [0]=> float(10.2) }
19string(5) "[010]" NULL
'Invalid data'
NULL array(1) { [0]=> int(10) } NULL
'Octal notation not supported by JSON (value: 010)'
20string(6) "[0xFF]" array(1) { [0]=> int(255) } NULL array(1) { [0]=> int(0) } NULL
'Illegal Token'
21string(6) "[0xff]" array(1) { [0]=> int(255) } NULL array(1) { [0]=> int(0) } NULL
'Illegal Token'
22string(6) "[true]" array(1) { [0]=> bool(true) } array(1) { [0]=> bool(true) } array(1) { [0]=> bool(true) } array(1) { [0]=> bool(true) }
23string(6) "[TRUE]" array(1) { [0]=> bool(true) } NULL array(1) { [0]=> bool(true) } NULL
'Illegal Token'
24string(6) "[null]" array(1) { [0]=> NULL } array(1) { [0]=> NULL } array(1) { [0]=> NULL } array(1) { [0]=> NULL }
25string(6) "[NULL]" array(1) { [0]=> NULL } NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
26string(4) "[""]" array(1) { [0]=> string(0) "" } array(1) { [0]=> string(0) "" } array(1) { [0]=> string(0) "" } array(1) { [0]=> string(0) "" }
27string(5) "["a"]" array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" }
28string(9) " [ "a" ] " array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" }
29string(4) "[1,]" array(2) { [0]=> int(1) [1]=> NULL } NULL array(2) { [0]=> int(1) [1]=> NULL } array(1) { [0]=> int(1) }
30string(3) "[,]" array(2) { [0]=> NULL [1]=> NULL } NULL array(2) { [0]=> NULL [1]=> NULL } array(1) { [0]=> NULL }
31string(4) "[,1]" array(2) { [0]=> NULL [1]=> int(1) } NULL array(2) { [0]=> NULL [1]=> int(1) } array(2) { [0]=> NULL [1]=> int(1) }
32string(6) "[1,,1]" array(3) { [0]=> int(1) [1]=> NULL [2]=> int(1) } NULL array(3) { [0]=> int(1) [1]=> NULL [2]=> int(1) } array(3) { [0]=> int(1) [1]=> NULL [2]=> int(1) }
33string(19) "// comment here [1]" NULL
'Invalid data'
string(19) "// comment here [1]" array(1) { [0]=> int(1) } NULL
'Illegal Token'
34string(19) "[// comment here 1]" NULL
'Invalid data'
NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
35string(19) "[1// comment here ]" NULL
'Invalid data'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
36string(19) "[1]// comment here " NULL
'Invalid data (array missing closing bracket?)'
NULL NULL NULL
'Illegal Token'
37string(19) "/*comment here*/[1]" array(1) { [0]=> int(1) } string(19) "/*comment here*/[1]" array(1) { [0]=> int(1) } NULL
'Illegal Token'
38string(19) "[/*comment here*/1]" array(1) { [0]=> int(1) } NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
39string(19) "[1/*comment here*/]" array(1) { [0]=> int(1) } NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
40string(19) "[1]/*comment here*/" array(1) { [0]=> int(1) } NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
41string(7) "/**/[1]" array(1) { [0]=> int(1) } string(7) "/**/[1]" NULL NULL
'Illegal Token'
42string(6) "// [1]" NULL
'Invalid data'
string(6) "// [1]" NULL NULL
'Illegal Token'
43string(20) "[1 // comment here ]" NULL
'Invalid data'
NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
44string(20) "[ // comment here 1]" NULL
'Invalid data'
NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
45string(18) "[1]// comment here" NULL
'Invalid data (array missing closing bracket?)'
NULL NULL NULL
'Illegal Token'
46string(19) "[1] // comment here" NULL
'Invalid data (array missing closing bracket?)'
NULL array(1) { [0]=> int(1) } NULL
'Illegal Token'
47string(28) "["a // this is a comment b"]" NULL
'Invalid data (line terminator char inside string?)'
NULL array(1) { [0]=> string(2) "ab" } array(1) { [0]=> string(24) "a // this is a comment b" }
48string(32) "["a // this is not a comment b"]" array(1) { [0]=> string(28) "a // this is not a comment b" } array(1) { [0]=> string(28) "a // this is not a comment b" } array(1) { [0]=> string(28) "a // this is not a comment b" } array(1) { [0]=> string(28) "a // this is not a comment b" }
49string(5) "['a']" array(1) { [0]=> string(1) "a" } NULL array(1) { [0]=> string(1) "a" } NULL
'Illegal Token'
50string(4) "["a]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } array(1) { [0]=> string(2) "a]" }
51string(4) "[a"]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
52string(5) "['a"]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
53string(5) "["a']" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } array(1) { [0]=> string(3) "a']" }
54string(3) "[']" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
55string(4) "['']" array(1) { [0]=> string(0) "" } NULL array(1) { [0]=> string(0) "" } NULL
'Illegal Token'
56string(5) "[''']" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } NULL
'Illegal Token'
57string(3) "["]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } array(1) { [0]=> string(1) "]" }
58string(5) "["""]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> NULL } NULL
'Missing "," in array encoding: ["""]'
59string(5) "["'"]" array(1) { [0]=> string(1) "'" } array(1) { [0]=> string(1) "'" } array(1) { [0]=> string(1) "'" } array(1) { [0]=> string(1) "'" }
60string(5) "['"']" array(1) { [0]=> string(1) """ } NULL array(1) { [0]=> string(1) """ } NULL
'Illegal Token'
61string(6) "["\'"]" array(1) { [0]=> string(1) "'" } NULL array(1) { [0]=> string(1) "'" } NULL
'Illegal escape sequence ''''
62string(7) "["\\'"]" array(1) { [0]=> string(2) "\'" } array(1) { [0]=> string(2) "\'" } array(1) { [0]=> string(2) "\'" } array(1) { [0]=> string(2) "\'" }
63string(6) "["\""]" array(1) { [0]=> string(1) """ } array(1) { [0]=> string(1) """ } array(1) { [0]=> string(1) """ } array(1) { [0]=> string(1) """ }
64string(5) "["\"]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> string(1) "\" } array(1) { [0]=> string(2) ""]" }
65string(6) "["\\"]" array(1) { [0]=> string(1) "\" } array(1) { [0]=> string(1) "\" } array(1) { [0]=> string(1) "\" } array(1) { [0]=> string(1) "\" }
66string(7) "["\\\"]" NULL
'Invalid data (string missing closing quote?)'
NULL array(1) { [0]=> string(2) "\\" } array(1) { [0]=> string(3) "\"]" }
67string(6) " ["a"]" array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" }
68string(6) "[ "a"]" array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" }
69string(6) "["a "]" NULL
'Invalid data (line terminator char inside string?)'
NULL array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(2) "a " }
70string(6) "["a" ]" array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" }
71string(6) "["a"] " array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" } array(1) { [0]=> string(1) "a" }
72string(16) "["\u0041\u00DC"]" array(1) { [0]=> string(2) "AÜ" } array(1) { [0]=> string(3) "AÃœ" } array(1) { [0]=> string(3) "AÃœ" } NULL
'Illegal escape sequence 'u''
73string(16) "["\b\t\f\v\r\n"]" array(1) { [0]=> string(6) " " } NULL array(1) { [0]=> string(7) " \v " } NULL
'Illegal escape sequence 'v''
74string(14) "["\b\t\f\r\n"]" array(1) { [0]=> string(5) " " } array(1) { [0]=> string(5) " " } array(1) { [0]=> string(5) " " } array(1) { [0]=> string(5) " " }
75string(12) "["\x41\xDC"]" array(1) { [0]=> string(2) "AÜ" } NULL array(1) { [0]=> string(8) "\x41\xDC" } NULL
'Illegal escape sequence 'x''
76string(7) "[ ] [ ]" NULL
'Invalid data (unmatched array closing bracket?)'
NULL array(1) { [0]=> NULL } array(0) { }
77string(9) "["a" "b"]" NULL
'Invalid data (unescaped quote char inside string?)'
NULL array(1) { [0]=> string(5) "a" "b" } NULL
'Missing "," in array encoding: ["a" "b"]'
78string(5) "[1 2]" NULL
'Invalid data'
NULL array(1) { [0]=> NULL } NULL
'Missing "," in array encoding: [1 2]'
79string(4) "[{}]" array(1) { [0]=> array(0) { } } array(1) { [0]=> object(stdClass)#6 (0) { } } array(1) { [0]=> array(0) { } } array(1) { [0]=> array(0) { } }
80string(7) "[ { } ]" array(1) { [0]=> array(0) { } } array(1) { [0]=> object(stdClass)#5 (0) { } } array(1) { [0]=> array(0) { } } array(1) { [0]=> array(0) { } }
81string(5) "[{1}]" NULL
'Invalid data (missing object member name?)'
NULL array(1) { [0]=> array(0) { } } NULL
'Missing key in object encoding: [{1}]'
82string(7) "[{1:1}]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Missing key in object encoding: [{1:1}]'
83string(6) "[{:1}]" NULL
'Invalid data (missing object member name?)'
NULL array(1) { [0]=> array(0) { } } NULL
'Missing key in object encoding: [{:1}]'
84string(8) "[{"1":}]" NULL
'Invalid data (empty string?)'
NULL array(1) { [0]=> array(0) { } } array(1) { [0]=> array(1) { [1]=> NULL } }
85string(9) "[{"1":1}]" array(1) { [0]=> array(1) { [1]=> int(1) } } array(1) { [0]=> object(stdClass)#5 (1) { ["1"]=> int(1) } } array(1) { [0]=> array(1) { [1]=> int(1) } } array(1) { [0]=> array(1) { [1]=> int(1) } }
86string(8) "[{"":1}]" array(1) { [0]=> array(1) { [""]=> int(1) } } array(1) { [0]=> object(stdClass)#6 (1) { ["_empty_"]=> int(1) } } array(1) { [0]=> array(0) { } } array(1) { [0]=> array(1) { [""]=> int(1) } }
87string(8) "[{"a":}]" NULL
'Invalid data (empty string?)'
NULL array(1) { [0]=> array(0) { } } array(1) { [0]=> array(1) { ["a"]=> NULL } }
88string(9) "[{"a":1}]" array(1) { [0]=> array(1) { ["a"]=> int(1) } } array(1) { [0]=> object(stdClass)#6 (1) { ["a"]=> int(1) } } array(1) { [0]=> array(1) { ["a"]=> int(1) } } array(1) { [0]=> array(1) { ["a"]=> int(1) } }
89string(12) "[{"true":1}]" array(1) { [0]=> array(1) { ["true"]=> int(1) } } array(1) { [0]=> object(stdClass)#5 (1) { ["true"]=> int(1) } } array(1) { [0]=> array(1) { ["true"]=> int(1) } } array(1) { [0]=> array(1) { ["true"]=> int(1) } }
90string(10) "[{true:1}]" array(1) { [0]=> array(1) { ["true"]=> int(1) } } NULL array(1) { [0]=> array(1) { ["true"]=> int(1) } } NULL
'Missing key in object encoding: [{true:1}]'
91string(10) "[{null:1}]" array(1) { [0]=> array(1) { ["null"]=> int(1) } } NULL array(1) { [0]=> array(1) { ["null"]=> int(1) } } NULL
'Missing key in object encoding: [{null:1}]'
92string(11) "[{a "a":1}]" array(1) { [0]=> array(1) { ["a "a""]=> int(1) } } NULL array(1) { [0]=> array(0) { } } NULL
'Illegal Token'
93string(12) "[{"a":1"a"}]" NULL
'Invalid data'
NULL array(1) { [0]=> array(1) { ["a"]=> NULL } } NULL
'Missing "," in object encoding: [{"a":1"a"}]'
94string(11) "[{a b:"a"}]" array(1) { [0]=> array(1) { ["a b"]=> string(1) "a" } } NULL array(1) { [0]=> array(0) { } } NULL
'Illegal Token'
95string(11) "[{a b:a b}]" NULL
'Invalid data'
NULL array(1) { [0]=> array(0) { } } NULL
'Illegal Token'
96string(11) "[{a 1:a 1}]" NULL
'Invalid data'
NULL array(1) { [0]=> array(0) { } } NULL
'Illegal Token'
97string(9) "[{a:b:c}]" NULL
'Invalid data'
NULL array(1) { [0]=> array(1) { ["a"]=> NULL } } NULL
'Illegal Token'
98string(25) "[/*comment here*/{"1":1}]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Illegal Token'
99string(25) "[{/*comment here*/"1":1}]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Illegal Token'
100string(25) "[{"1"/*comment here*/:1}]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Illegal Token'
101string(25) "[{"1":/*comment here*/1}]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Illegal Token'
102string(25) "[{"1":1/*comment here*/}]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Illegal Token'
103string(25) "[{"1":1}/*comment here*/]" array(1) { [0]=> array(1) { [1]=> int(1) } } NULL array(1) { [0]=> array(1) { [1]=> int(1) } } NULL
'Illegal Token'

Values encoding tests

 ValueXmlRpc EncodedNative enc.Pear enc.Zend enc.
0bool(true) string(4) "true" string(4) "true" string(4) "true" string(4) "true"
1bool(false) string(5) "false" string(5) "false" string(5) "false" string(5) "false"
2int(0) string(1) "0" string(1) "0" int(0) string(1) "0"
3int(1) string(1) "1" string(1) "1" int(1) string(1) "1"
4float(1) string(3) "1.0" string(1) "1" float(1) string(1) "1"
5float(1.1) string(3) "1.1" string(3) "1.1" float(1.1) string(3) "1.1"
6string(0) "" string(2) """" string(2) """" string(2) """" string(2) """"
7NULL string(4) "null" string(4) "null" string(4) "null" string(4) "null"
8string(1) "1" string(3) ""1"" string(3) ""1"" string(3) ""1"" string(3) ""1""
9string(17) "20060101T12:00:00" string(19) ""20060101T12:00:00"" string(19) ""20060101T12:00:00"" string(19) ""20060101T12:00:00"" string(19) ""20060101T12:00:00""
10string(15) "Günter, Elène" string(25) ""G\u00fcnter, El\u00e8ne"" string(25) ""G\u00fcnter, El\u00e8ne"" string(25) ""G\u00fcnter, El\u00e8ne"" string(17) ""Günter, Elène""
11string(13) "Günter, Elène" string(17) ""Gnter, El\u8ba5"" string(3) ""G"" string(38) ""G\u006e007400650072002c El\u006e0065"" string(15) ""Günter, Elène""
12resource(9) of type (stream) string(4) "null"
Warning: [json] (json_encode_r) type is unsupported, encoded as null. in C:\htdocs\xmlrpc_cvs\extras\jsonrpc\testsuite3.php on line 382
string(4) "null"
NULL string(4) "null"
13string(8) "aGVsbG8=" string(10) ""aGVsbG8="" string(10) ""aGVsbG8="" string(10) ""aGVsbG8="" string(10) ""aGVsbG8=""
14object(jsonrpcmsg)#2 (6) { ["id"]=> NULL ["content_type"]=> string(16) "application/json" ["payload"]=> NULL ["methodname"]=> string(5) "dummy" ["params"]=> array(0) { } ["debug"]=> int(0) } string(104) "{"id":null,"content_type":"application\/json","payload":null,"methodname":"dummy","params":[],"debug":0}" string(104) "{"id":null,"content_type":"application\/json","payload":null,"methodname":"dummy","params":[],"debug":0}" string(104) "{"id":null,"content_type":"application\/json","payload":null,"methodname":"dummy","params":[],"debug":0}" string(105) "{"__className":"jsonrpcmsg","content_type":"application/json","methodname":"dummy","params":[],"debug":0}"
15object(xmlrpcval)#3 (3) { ["me"]=> array(0) { } ["mytype"]=> int(0) ["_php_class"]=> NULL } string(38) "{"me":[],"mytype":0,"_php_class":null}" string(38) "{"me":[],"mytype":0,"_php_class":null}" string(38) "{"me":[],"mytype":0,"_php_class":null}" string(46) "{"__className":"xmlrpcval","me":[],"mytype":0}"
16array(0) { } string(2) "[]" string(2) "[]" string(2) "[]" string(2) "[]"
17array(1) { [0]=> string(15) "Günter, Elène" } string(27) "["G\u00fcnter, El\u00e8ne"]" string(27) "["G\u00fcnter, El\u00e8ne"]" string(27) "["G\u00fcnter, El\u00e8ne"]" string(19) "["Günter, Elène"]"
18array(1) { [0]=> string(1) "a" } string(5) "["a"]" string(5) "["a"]" string(5) "["a"]" string(5) "["a"]"
19array(1) { [0]=> array(1) { [0]=> int(1) } } string(5) "[[1]]" string(5) "[[1]]" string(5) "[[1]]" string(5) "[[1]]"
20array(2) { [2]=> bool(true) [3]=> bool(false) } string(20) "{"2":true,"3":false}" string(20) "{"2":true,"3":false}" string(20) "{"2":true,"3":false}" string(20) "{"2":true,"3":false}"
21array(1) { ["hello"]=> string(5) "world" } string(17) "{"hello":"world"}" string(17) "{"hello":"world"}" string(17) "{"hello":"world"}" string(17) "{"hello":"world"}"
22array(2) { ["hello"]=> bool(true) [0]=> string(5) "world" } string(26) "{"hello":true,"0":"world"}" string(26) "{"hello":true,"0":"world"}" string(26) "{"hello":true,"0":"world"}" string(26) "{"hello":true,"0":"world"}"
23array(3) { ["hello"]=> bool(true) [0]=> string(5) "hello" [1]=> string(5) "world" } string(38) "{"hello":true,"0":"hello","1":"world"}" string(38) "{"hello":true,"0":"hello","1":"world"}" string(38) "{"hello":true,"0":"hello","1":"world"}" string(38) "{"hello":true,"0":"hello","1":"world"}"
24array(2) { ["methodname"]=> string(5) "hello" ["params"]=> array(0) { } } string(34) "{"methodname":"hello","params":[]}" string(34) "{"methodname":"hello","params":[]}" string(34) "{"methodname":"hello","params":[]}" string(34) "{"methodname":"hello","params":[]}"
25array(2) { ["faultCode"]=> int(666) ["faultString"]=> string(11) "hello world" } string(45) "{"faultCode":666,"faultString":"hello world"}" string(45) "{"faultCode":666,"faultString":"hello world"}" string(45) "{"faultCode":666,"faultString":"hello world"}" string(45) "{"faultCode":666,"faultString":"hello world"}"
26array(3) { ["faultCode"]=> int(666) ["faultString"]=> string(11) "hello world" ["faultWhat?"]=> string(5) "dunno" } string(66) "{"faultCode":666,"faultString":"hello world","faultWhat?":"dunno"}" string(66) "{"faultCode":666,"faultString":"hello world","faultWhat?":"dunno"}" string(66) "{"faultCode":666,"faultString":"hello world","faultWhat?":"dunno"}" string(66) "{"faultCode":666,"faultString":"hello world","faultWhat?":"dunno"}"
27array(2) { ["faultCode"]=> int(666) ["faultString"]=> array(1) { [0]=> string(11) "hello world" } } string(47) "{"faultCode":666,"faultString":["hello world"]}" string(47) "{"faultCode":666,"faultString":["hello world"]}" string(47) "{"faultCode":666,"faultString":["hello world"]}" string(47) "{"faultCode":666,"faultString":["hello world"]}"
28array(2) { ["faultCode"]=> int(666) ["faultString"]=> array(1) { ["hello"]=> string(5) "world" } } string(49) "{"faultCode":666,"faultString":{"hello":"world"}}" string(49) "{"faultCode":666,"faultString":{"hello":"world"}}" string(49) "{"faultCode":666,"faultString":{"hello":"world"}}" string(49) "{"faultCode":666,"faultString":{"hello":"world"}}"

And the winner is...

Of course there cannot be a winner. Every lib test excels in some aspect and is surpassed in others.

The PHP native JSON extension cannot be beaten for speed, but it is very unforgiving on the json it parses as valid. A sure hit for almost everybody.

The Zend lib is the fastest of the php-based libs, and shows no major drawbacks, excepted for lack of support for php 4 (and some encoding quirks, that will undoubtely be fixed in future versions of the package). A solid recommendation for those on shared hosting or in the impossibility to install the extension.

The php-jsonrpc lib is the slowest of the pack at encoding php values (because of its object-centric two step conversion process), but sports more features than any other lib, such as automatic charset conversion or extra support for jsonrpc calls.
It is also the closest to the browsers javascript engines in its json parsing results.

The PEAR lib offers no clear advantage, but it still makes for a valid choice, providing the most forgiving json parser of all.

Discuss

Comments, corrections, suggestions for improvement are welcome. A separate blog entry is available for dicussing this comparison.


Changes

2007/04/25: published the revised comparison page

Notes
  1. The check for recursive arrays/objects works fine an any given single php variable, but returns false positives when a single php value is element of two or more different arrays. This is due to inherent limitations of the php engine.
  2. The usefullness of this feature is seriously hampered by the fact that the API exposed by the native json extension is slightly different from the API of the Zend lib, e.g. vis-a-vis treatment of UTF-8 encoded data
  3. All null members of an object are not encoded in the json representation, while an extra member, named "__className" is added
  4. Some of the strings to be decoded have (unescaped) line break chars inside, which poses quite a problem to the javascript spec. Those chars are rendered as plain whitespace by the browser