简体   繁体   English

PHP/json_encode:处理具有数字属性的混合数组和对象

[英]PHP/json_encode: dealing with mixed arrays and objects with numeric properties

I recently had to tackle a bug in a legacy PHP application.我最近不得不解决遗留 PHP 应用程序中的错误。 This application receives a request from another application with JSON of the form:此应用程序接收来自另一个应用程序的请求,其 JSON 格式为:

{
  "someList": [
    "item A",
    "item B"
  ],
  "ratings": {
    "0": 0.001234,
    "1": 0.0666,
    "2": 0.09876,
    "3": 0.777777
  }
}

When this is deserialized to a native PHP "associative array", both the list and the map (with keys 0, 1, 2, and 3) look like lists.当它被反序列化为原生 PHP “关联数组”时,列表和映射(带有键 0、1、2 和 3)看起来都像列表。 That's fine, I can work around that.没关系,我可以解决这个问题。 However, this application does calculations on this data and adds some more to it before serializing back to JSON in roughly the same format and sends it along to another application.但是,此应用程序会对该数据进行计算,并在以大致相同的格式将其序列化回 JSON 并将其发送到另一个应用程序之前对其进行更多计算。 Here's where the problem is.这就是问题所在。 Out of the box json_encode($data) of the above results in:上面的开箱即用json_encode($data)结果:

{
  "someList": [
    "item A",
    "item B"
  ],
  "ratings": [
    0.001234,
    0.0666,
    0.09876,
    0.777777
  ]
}

My keys are all gone...我的钥匙都没了...

I see that I can use JSON_FORCE_OBJECT a la echo json_encode($data, JSON_FORCE_OBJECT) but then I get:我看到我可以使用JSON_FORCE_OBJECT a la echo json_encode($data, JSON_FORCE_OBJECT)但后来我得到:

{
  "someList": {
    "0": "item A",
    "1": "item B"
  },
  "ratings": {
    "0": 0.001234,
    "1": 0.0666,
    "2": 0.09876,
    "3": 0.777777
  }
}

Now I've got keys in the first list, which I don't want.现在我在第一个列表中得到了我不想要的键。 Is there a way to serialize this JSON such that someList will be a list (no keys) and ratings will be a map/object (with keys 0, 1, 2, and 3)?有没有办法序列化这个 JSON,使得someList将是一个列表(没有键),而ratings将是一个地图/对象(带有键 0、1、2 和 3)?

When calling json_encode on an array list, with numeric coherent indexes starting with 0, PHP will treat the list as an indexed array, and not an associative array.当在数组列表上调用 json_encode 时,数字一致索引从 0 开始,PHP 会将列表视为索引数组,而不是关联数组。 To force php to treat it as an associative array, you can cast the array to an object before calling json_encode.要强制 php 将其视为关联数组,您可以在调用 json_encode 之前将数组转换为对象。

Example:例子:

$somelist = ["item A", "item B"];
$ratings = [0.001234, 0.666, 0.09876, 0.777777];
$final = ['someList' => $somelist, 'ratings' => (object) $ratings];

echo json_encode($final);

Output:输出:

{["item A","item B"],{"0":0.001234,"1":0.666,"2":0.09876,"3":0.777777}}

I've also struggled with returning JSON with both regular arrays and objects with numeric keys in the same response.我也一直在努力在同一响应中返回带有常规数组和带有数字键的对象的 JSON。

A solution I found is that you can build up a stdObject and defining the keys using $obj->{'0'} for example.我发现的一个解决方案是,您可以构建一个 stdObject 并使用例如$obj->{'0'}定义键。

Here's a full example:这是一个完整的例子:


$decoded = json_decode('{
  "someList": [
    "item A",
    "item B"
  ],
  "ratings": {
    "0": 0.001234,
    "1": 0.0666,
    "2": 0.09876,
    "3": 0.777777
  }
}', true);

// Do stuff with $decoded['ratings']

$ratings = new \stdClass;
foreach ($decoded['ratings'] as $key => $rating) {
    $ratings->{$key} = $rating;
}

echo json_encode([
    'someList' => $decoded['someList'],
    'ratings' => $ratings
]);

Which then will output the following:然后将输出以下内容:

{
  "someList": [
    "item A",
    "item B"
  ],
  "ratings": {
    "0": 0.001234,
    "1": 0.0666,
    "2": 0.09876,
    "3": 0.777777
  }
}

I wouldn't normally suggest "building" JSON manually, but concatenating bits of valid JSON should be fairly safe, and I think it's the only way you'll get this working.我通常不会建议手动“构建”JSON,但连接有效 JSON 的位应该是相当安全的,而且我认为这是您让它工作的唯一方法。

$somelist = ["item A", "item B"];
$ratings = [0.001234, 0.666, 0.09876, 0.777777];

$json = sprintf(
    '{"somelist":%s,"ratings":%s}',
    json_encode($somelist),
    json_encode($ratings, JSON_FORCE_OBJECT)
);
echo $json;

Or in case you have a larger object to work with, you can loop over the data to do this programmatically.或者,如果您有一个更大的对象要处理,您可以循环遍历数据以编程方式执行此操作。

$original_json = '{"someList":["item A","item B"],"ratings""{"0":0.001234,"1":0.0666,"2":0.09876,"3":0.777777}}';
$data = json_decode($original_json);
// do whatever you need to do with the data
array_walk($data->someList, function(&$v, $k){$v .= " is changed";});

$vsprintf_args = [];
$format_str = "{";
foreach($data as $k=>$v) {
    $format_str .= '%s:%s,';
    $vsprintf_args[] = json_encode($k);
    $vsprintf_args[] = json_encode($v, ($k === "ratings" ? JSON_FORCE_OBJECT : 0));
}
$format_str = trim($format_str, ",") . "}";
$json = vsprintf($format_str, $vsprintf_args);
echo $json;

Output:输出:

{"somelist":["item A","item B"],"ratings":{"0":0.001234,"1":0.666,"2":0.09876,"3":0.777777}}

Was able to find a solution by using stdClass instead of an associative array when decoding the original JSON via json_decode($json, false);在通过json_decode($json, false);解码原始 JSON 时,能够通过使用stdClass而不是关联数组找到解决方案json_decode($json, false); . . Then when json_encode ing the resulting stdClass the keys will be preserved.然后当json_encode ing 生成的stdClass ,键将被保留。

Here's a full example:这是一个完整的例子:

<?php

$json = <<<JSON
{
  "someList": [
    "item A",
    "item B"
  ],
  "ratings": {
    "0": 0.001234,
    "1": 0.0666,
    "2": 0.09876,
    "3": 0.777777
  }
}
JSON;

// Passing false for the second param (or omitting it)
// returns a stdClass instead of associative array
$data = json_decode($json, false);

echo json_encode($data, JSON_PRETTY_PRINT);

Which outputs:哪些输出:

{
    "someList": [
        "item A",
        "item B"
    ],
    "ratings": {
        "0": 0.001234,
        "1": 0.0666,
        "2": 0.09876,
        "3": 0.777777
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM