Skip to content

Commit 5ecae59

Browse files
committed
[api] Do not double escape json values
1 parent 672818d commit 5ecae59

5 files changed

Lines changed: 109 additions & 4 deletions

File tree

tests/AcceptApiTest.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public function testGetConstituencyByName() {
6767
'getConstituency',
6868
'name',
6969
'Amber Valley',
70-
'{"name":"Amber Valley"}'
70+
'{"demographics":{"population":72000,"age_groups":{"18-35":25,"36-55":40,"56+":35}},"description":"A parliamentary constituency in Derbyshire","name":"Amber Valley"}'
7171
);
7272
}
7373

@@ -140,6 +140,68 @@ public function testGetMlasLookup() {
140140
);
141141
}
142142

143+
public function testGetMPsInfoJsonEscaping() {
144+
// Test that JSON stored in personinfo is not double-encoded when requesting JSON output
145+
$page = $this->fetch_page('getMPsInfo', [
146+
'key' => 'test_key',
147+
'id' => '2,3',
148+
'output' => 'json',
149+
]);
150+
151+
$response = json_decode($page, true);
152+
$this->assertNotNull($response, 'Response should be valid JSON');
153+
154+
// Check that the JSON field is properly parsed as an object, not escaped as a string
155+
$this->assertIsArray($response['2']['test_json_field'], 'JSON field should be parsed as array/object, not string');
156+
$this->assertEquals('Test Person', $response['2']['test_json_field']['name']);
157+
$this->assertEquals('London', $response['2']['test_json_field']['location']);
158+
$this->assertIsArray($response['2']['test_json_field']['interests']);
159+
160+
// Check that plain text fields remain as strings
161+
$this->assertIsString($response['2']['test_plain_field']);
162+
$this->assertEquals('Simple text value', $response['2']['test_plain_field']);
163+
164+
// Check that JSON arrays are properly parsed
165+
$this->assertIsArray($response['3']['test_json_array']);
166+
$this->assertEquals(1, $response['3']['test_json_array'][0]['id']);
167+
$this->assertEquals('Item 1', $response['3']['test_json_array'][0]['name']);
168+
}
169+
170+
public function testGetMPsInfoXmlOutput() {
171+
// Test that XML output still works with JSON content (should be escaped in XML)
172+
$page = $this->fetch_page('getMPsInfo', [
173+
'key' => 'test_key',
174+
'id' => '2',
175+
'output' => 'xml',
176+
]);
177+
178+
$this->assertStringContainsString('<test_json_field>', $page);
179+
// In XML, JSON content should be escaped/as string with HTML entities
180+
$this->assertStringContainsString('&quot;name&quot;: &quot;Test Person&quot;', $page);
181+
}
182+
183+
public function testGetConstituencyInfoJsonEscaping() {
184+
// Test that JSON stored in consinfo is not double-encoded when requesting JSON output
185+
$page = $this->fetch_page('getConstituency', [
186+
'key' => 'test_key',
187+
'name' => 'Amber Valley',
188+
'output' => 'json',
189+
]);
190+
191+
$response = json_decode($page, true);
192+
$this->assertNotNull($response, 'Response should be valid JSON');
193+
194+
// Check that the JSON field is properly parsed as an object, not escaped as a string
195+
$this->assertIsArray($response['demographics'], 'JSON field should be parsed as array/object, not string');
196+
$this->assertEquals(72000, $response['demographics']['population']);
197+
$this->assertIsArray($response['demographics']['age_groups']);
198+
$this->assertEquals(25, $response['demographics']['age_groups']['18-35']);
199+
200+
// Check that plain text fields remain as strings
201+
$this->assertIsString($response['description']);
202+
$this->assertEquals('A parliamentary constituency in Derbyshire', $response['description']);
203+
}
204+
143205
public function testApiKeySignup() {
144206
$page = $this->post_page('key');
145207
$this->assertStringContainsString('Subscribe to a plan', $page);

tests/_fixtures/api.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@
3030
<table_data name="comments">
3131
</table_data>
3232
<table_data name="consinfo">
33+
<row>
34+
<field name="constituency">Amber Valley</field>
35+
<field name="data_key">demographics</field>
36+
<field name="data_value">{"population": 72000, "age_groups": {"18-35": 25, "36-55": 40, "56+": 35}}</field>
37+
</row>
38+
<row>
39+
<field name="constituency">Amber Valley</field>
40+
<field name="data_key">description</field>
41+
<field name="data_value">A parliamentary constituency in Derbyshire</field>
42+
</row>
3343
</table_data>
3444
<table_data name="constituency">
3545
<row>
@@ -184,6 +194,21 @@
184194
</row>
185195
</table_data>
186196
<table_data name="personinfo">
197+
<row>
198+
<field name="person_id">2</field>
199+
<field name="data_key">test_json_field</field>
200+
<field name="data_value">{"name": "Test Person", "location": "London", "interests": ["politics", "technology"]}</field>
201+
</row>
202+
<row>
203+
<field name="person_id">2</field>
204+
<field name="data_key">test_plain_field</field>
205+
<field name="data_value">Simple text value</field>
206+
</row>
207+
<row>
208+
<field name="person_id">3</field>
209+
<field name="data_key">test_json_array</field>
210+
<field name="data_value">[{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}]</field>
211+
</row>
187212
</table_data>
188213
<table_data name="postcode_lookup">
189214
<row>

www/docs/api/api_functions.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,22 @@ function api_sidebar($subscription) {
223223
return $sidebar;
224224
}
225225

226+
# Utility functions
227+
228+
function api_decode_value($data_value) {
229+
// For JSON-based outputs (json, default JS), decode stored JSON so it
230+
// isn't double-encoded. For xml/php/rabx, return as-is.
231+
$output = get_http_var('output');
232+
if (in_array($output, ['xml', 'php', 'rabx'])) {
233+
return $data_value;
234+
}
235+
$json_decoded = json_decode($data_value, true);
236+
if (json_last_error() === JSON_ERROR_NONE && $json_decoded !== null) {
237+
return $json_decoded;
238+
}
239+
return $data_value;
240+
}
241+
226242
# Output functions
227243

228244
function api_output($arr, $last_mod = null) {

www/docs/api/api_getConstituency.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function _api_getConstituency_name($constituency) {
6262
$output = [];
6363
foreach ($q as $row) {
6464
$data_key = $row['data_key'];
65-
$output[$data_key] = $row['data_value'];
65+
$output[$data_key] = api_decode_value($row['data_value']);
6666
}
6767
ksort($output);
6868
$output['name'] = $constituency;

www/docs/api/api_getMPsInfo.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ function _api_getMPsInfo_id($ids) {
3838
continue;
3939
}
4040
$pid = $row['person_id'];
41-
$output[$pid][$data_key] = $row['data_value'];
41+
$data_value = api_decode_value($row['data_value']);
42+
$output[$pid][$data_key] = $data_value;
4243
$time = strtotime($row['lastupdate']);
4344
if ($time > $last_mod) {
4445
$last_mod = $time;
@@ -61,7 +62,8 @@ function _api_getMPsInfo_id($ids) {
6162
if (!isset($output[$pid]['by_member_id'][$mid])) {
6263
$output[$pid]['by_member_id'][$mid] = [];
6364
}
64-
$output[$pid]['by_member_id'][$mid][$data_key] = $row['data_value'];
65+
66+
$output[$pid]['by_member_id'][$mid][$data_key] = api_decode_value($row['data_value']);
6567
$time = strtotime($row['lastupdate']);
6668
if ($time > $last_mod) {
6769
$last_mod = $time;

0 commit comments

Comments
 (0)