API CHANGE: JSONDataFormatter builds JSON itself, changing it to use Convert::array2json() instead (fixes #5162, thanks sharvey)

This commit is contained in:
Julian Seidenberg 2011-03-23 10:23:57 +13:00 committed by Ingo Schommer
parent 7ebe602482
commit a204c136fe
4 changed files with 83 additions and 36 deletions

View File

@ -26,27 +26,38 @@ class JSONDataFormatter extends DataFormatter {
}
/**
* Generate an XML representation of the given {@link DataObject}.
*
* @param DataObject $obj
* @param $includeHeader Include <?xml ...?> header (Default: true)
* @return String XML
* Generate a JSON representation of the given {@link DataObject}.
*
* @param DataObject $obj The object
* @param Array $fields If supplied, only fields in the list will be returned
* @param $relations Not used
* @return String JSON
*/
public function convertDataObject(DataObjectInterface $obj, $fields = null, $relations = null) {
return Convert::array2json($this->convertDataObjectToJSONObject($obj, $fields, $relations));
}
/**
* Internal function to do the conversion of a single data object. It builds an empty object and dynamically
* adds the properties it needs to it. If it's done as a nested array, json_encode or equivalent won't use
* JSON object notation { ... }.
* @param DataObjectInterface $obj
* @param $fields
* @param $relations
* @return EmptyJSONObject
*/
public function convertDataObjectToJSONObject(DataObjectInterface $obj, $fields = null, $relations = null) {
$className = $obj->class;
$id = $obj->ID;
$json = "{\n \"className\" : \"$className\",\n";
$serobj = ArrayData::array_to_object();
foreach($this->getFieldsForObj($obj) as $fieldName => $fieldType) {
// Field filtering
if($fields && !in_array($fieldName, $fields)) continue;
$fieldValue = $obj->$fieldName;
if(is_object($fieldValue) && is_subclass_of($fieldValue, 'Object') && $fieldValue->hasMethod('toJSON')) {
$jsonParts[] = "\"$fieldName\" : " . $fieldValue->toJSON();
} else {
$jsonParts[] = "\"$fieldName\" : " . Convert::raw2json($fieldValue);
}
$serobj->$fieldName = $fieldValue;
}
if($this->relationDepth > 0) {
@ -63,24 +74,24 @@ class JSONDataFormatter extends DataFormatter {
} else {
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
}
$jsonParts[] = "\"$relName\" : { \"className\" : \"$relClass\", \"href\" : \"$href.json\", \"id\" : \"{$obj->$fieldName}\" }";
$serobj->$relName = ArrayData::array_to_object(array("className" => $relClass, "href" => "$href.json", "id" => $obj->$fieldName));
}
foreach($obj->has_many() as $relName => $relClass) {
if(!singleton($relClass)->stat('api_access')) continue;
// Field filtering
if($fields && !in_array($relName, $fields)) continue;
if($this->customRelations && !in_array($relName, $this->customRelations)) continue;
$jsonInnerParts = array();
$innerParts = array();
$items = $obj->$relName();
foreach($items as $item) {
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
$jsonInnerParts[] = "{ \"className\" : \"$relClass\", \"href\" : \"$href.json\", \"id\" : \"{$obj->$fieldName}\" }";
$innerParts[] = ArrayData::array_to_object(array("className" => $relClass, "href" => "$href.json", "id" => $obj->$fieldName));
}
$jsonParts[] = "\"$relName\" : [\n " . implode(",\n ", $jsonInnerParts) . " \n ]";
$serobj->$relName = $innerParts;
}
foreach($obj->many_many() as $relName => $relClass) {
@ -90,42 +101,41 @@ class JSONDataFormatter extends DataFormatter {
if($fields && !in_array($relName, $fields)) continue;
if($this->customRelations && !in_array($relName, $this->customRelations)) continue;
$jsonInnerParts = array();
$innerParts = array();
$items = $obj->$relName();
foreach($items as $item) {
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
$jsonInnerParts[] = " { \"className\" : \"$relClass\", \"href\" : \"$href.json\", \"id\" : \"{$obj->$fieldName}\" }";
$innerParts[] = ArrayData::array_to_object(array("className" => $relClass, "href" => "$href.json", "id" => $obj->$fieldName));
}
$jsonParts[] = "\"$relName\" : [\n " . implode(",\n ", $jsonInnerParts) . "\n ]";
$serobj->$relName = $innerParts;
}
}
return "{\n " . implode(",\n ", $jsonParts) . "\n}"; }
return $serobj;
}
/**
* Generate an XML representation of the given {@link DataObjectSet}.
* Generate a JSON representation of the given {@link DataObjectSet}.
*
* @param DataObjectSet $set
* @return String XML
*/
public function convertDataObjectSet(DataObjectSet $set, $fields = null) {
$jsonParts = array();
foreach($set as $item) {
if($item->canView()) $jsonParts[] = $this->convertDataObject($item, $fields);
}
$json = "{\n";
$json .= '"totalSize": ';
$json .= (is_numeric($this->totalSize)) ? $this->totalSize : 'null';
$json .= ",\n";
$json .= "\"items\": [\n" . implode(",\n", $jsonParts) . "\n]\n";
$json .= "}\n";
$items = array();
foreach ($set as $do) $items[] = $this->convertDataObjectToJSONObject($do, $fields);
return $json;
$serobj = ArrayData::array_to_object(array(
"totalSize" => (is_numeric($this->totalSize)) ? $this->totalSize : null,
"items" => $items
));
return Convert::array2json($serobj);
}
public function convertStringToArray($strData) {
return Convert::json2array($strData);
}
}
}
?>

View File

@ -97,6 +97,18 @@ class ArrayData extends ViewableData {
return $arr;
}
/**
* Converts an associative array to a simple object
*
* @param array
* @return obj $obj
*/
public static function array_to_object($arr = null) {
$obj = new stdClass();
if ($arr) foreach($arr as $name => $value) $obj->$name = $value;
return $obj;
}
/**
* This is pretty crude, but it helps diagnose error situations

View File

@ -97,6 +97,15 @@ class ArrayDataTest extends SapphireTest {
$this->assertEquals($arrayData->getArray(), $array);
}
function testArrayToObject() {
$arr = array("test1" => "result1","test2"=>"result2");
$obj = ArrayData::array_to_object($arr);
$objExpected = new stdClass();
$objExpected->test1 = "result1";
$objExpected->test2 = "result2";
$this->assertEquals($obj,$objExpected, "Two objects match");
}
}
class ArrayDataTest_ArrayData_Exposed extends ArrayData {

View File

@ -357,6 +357,22 @@ class RestfulServerTest extends SapphireTest {
$this->assertEquals($responseArr['Rating'], 42);
$this->assertNotEquals($responseArr['WriteProtectedField'], 'haxx0red');
}
public function testJSONDataFormatter() {
$formatter = new JSONDataFormatter();
$single_do = $this->objFromFixture('Member', 'editor');
$this->assertEquals(
$formatter->convertDataObject($single_do, array("FirstName", "Email")),
'{"FirstName":"Editor","Email":"editor@test.com"}',
"Correct JSON formatting with field subset");
$set = DataObject::get("Member");
$this->assertEquals(
$formatter->convertDataObjectSet($set, array("FirstName", "Email")),
'{"totalSize":null,"items":[{"FirstName":"Editor","Email":"editor@test.com"},{"FirstName":"User","Email":"user@test.com"},{"FirstName":"ADMIN","Email":"ADMIN@example.org"}]}',
"Correct JSON formatting on a dataobjectset with field filter");
}
public function testApiAccessWithPOST() {
$url = "/api/v1/RestfulServerTest_AuthorRating";