FormatJson: Remove whitespace from empty arrays and objects

As noted in c370ad21d7, the pretty output can differ between
Zend PHP and HHVM. This change adds some post-processing to make
the output consistent across implementations and with JavaScript
JSON.stringify() and Python json.dumps(); all whitespace between
the opening and closing brackets/braces is removed.

Change-Id: I490e0ff1fac3d6c3fb44ab127e432872c0301a9d
This commit is contained in:
Kevin Israel 2013-10-08 17:23:06 -04:00 committed by Timo Tijhof
parent 39e22628f7
commit b6a5bb484d
2 changed files with 27 additions and 7 deletions

View file

@ -55,6 +55,17 @@ class FormatJson {
*/
const ALL_OK = 3;
/**
* Regex that matches whitespace inside empty arrays and objects.
*
* This doesn't affect regular strings inside the JSON because those can't
* have a real line break (\n) in them, at this point they are already escaped
* as the string "\n" which this doesn't match.
*
* @private
*/
const WS_CLEANUP_REGEX = '/(?<=[\[{])\n\s*+(?=[\]}])/';
/**
* Characters problematic in JavaScript.
*
@ -130,6 +141,12 @@ class FormatJson {
if ( $json === false ) {
return false;
}
if ( $pretty ) {
// Remove whitespace inside empty arrays/objects; different JSON encoders
// vary on this, and we want our output to be consistent across implementations.
$json = preg_replace( self::WS_CLEANUP_REGEX, '', $json );
}
if ( $escaping & self::UTF8_OK ) {
$json = str_replace( self::$badChars, self::$badCharsEscaped, $json );
}
@ -213,6 +230,7 @@ class FormatJson {
$buf .= substr( $json, $i, $skip );
}
}
return str_replace( "\x01", '\"', preg_replace( '/ +$/m', '', $buf ) );
$buf = preg_replace( self::WS_CLEANUP_REGEX, '', $buf );
return str_replace( "\x01", '\"', $buf );
}
}

View file

@ -12,28 +12,30 @@ class FormatJsonTest extends MediaWikiTestCase {
123,
456,
),
// Nested json works without problems
'"7":["8",{"9":"10"}]',
// Whitespace clean up doesn't touch strings that look alike
"{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}",
),
);
// 4 space indent, no trailing whitespace, no trailing linefeed
$json = '{
"emptyObject": {
},
"emptyArray": [
],
"emptyObject": {},
"emptyArray": [],
"string": "foobar\\\\",
"filledArray": [
[
123,
456
],
"\"7\":[\"8\",{\"9\":\"10\"}]"
"\"7\":[\"8\",{\"9\":\"10\"}]",
"{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}"
]
}';
$json = str_replace( "\r", '', $json ); // Windows compat
$this->assertSame( $json, str_replace("\n\n", "\n", FormatJson::encode( $obj, true ) ));
$this->assertSame( $json, FormatJson::encode( $obj, true ) );
}
public static function provideEncodeDefault() {