PHP Problem changing key values recursively in JSON array
Here is an exerpt from the php_decoded JSON structure that I am working with :
array(3) {
["$type"]=> string(51) "NanoWebInterpreter.WebInputData, NanoWebInterpreter"
["NBBList"]=>
array(2) {
["$type"]=> string(81) "System.Collections.Generic.List`1[[monoTNP.Common.NBB, monoTNP.Common]], mscorlib"
["$values"]=>
array(1) {
[0]=>
array(6) {
["$type"]=> string(34) "monoTNP.Common.NBB, monoTNP.Common"
["ID"]=> string(16) "id-0065-00000003"
["MPList"]=>
array(2) {
["$type"]=> string(80) "System.Collections.Generic.List`1[[monoTNP.Common.MP, monoTNP.Common]], mscorlib"
["$values"]=>
array(3) {
[0]=>
array(9) {
["$type"]=> string(43) "monoTNP.Common.EllipticalMP, monoTNP.Common"
["Eccentricity"]=> float(1)
["ID"]=> string(16) "id-0065-00000006"
["ParticleIndex"]=> int(-1)
["DispersionInteractionStrength"]=> float(0)
["DispersionInteractionRange"]=> float(2.5)
["CharacteristicSize"]=> float(0)
["CenterOfMass"]=> string(7) "<0,0,0>"
["OrientationVector"]=> string(2) "<>"
}
I have been trying to write this function that recursively traces the JSON object and replaces the target value with $postvalue, but whenever I try to do this recursively, the value isn't changed. Here is my code so far:
function replaceVal(&$json, $postkey, $postvalue, &$idCounter, $level)
{
$depth = 3;
#Base Case
#At the right depth level, check to see if the idCounter is equal to the
#postkey value (the HTML input field name). If it is, take the
#corresponding key and assign the postvalue to it (the input from form).
#Then return. Otherwise, incrememnt the idCounter and return.
if ($level >= $depth){
foreach($json as $key => $value){
if($idCounter == $postkey){
print "$key => $value\n";
$json[$key] = $postvalue; #Not working properly
return;
}
$idCounter++;
}
}
#Descend down into the level of the tree specified by $depth.
#This level should correspond do the level at which the HTML input
#fields lie
#$idCounter will only be greater than $postkey if the value has
#been changed by the previous if statement. In that case, the job is done
#and the function will terminate.
if ($level < $depth){
foreach($json as $key => $value){
if ($idCounter < $postkey)
replaceVal($value, $postkey, $postvalue, $idCounter, $level+1);
else
return;
}
}
}
The interesting part is that if I directly index into the structure like so:
$key = &$json['NBBList']['$values'][0]['MPList']['$values'][0]['Eccentricity']
$key = "asdf";
The value can be changed. The only thing that seems to be the problem is the recursion. This sounds like a really easy problem to fix, but I've only been programming for a little less than a year so I am probably just missing something obvious. >.>
Oh and the postvalue and postkey values come from an HTML form submission.
--edit-- The print statement is just in there for debugging. It can be ignored.
Edit 2: Here is how the function is called:
f开发者_如何学运维oreach ($_POST as $postkey => $postvalue)
{
if ($postvalue != ""){
print "$postkey => $postvalue\n";
$idCounter = 1;
replaceVal($json['NBBList']['$values'][0], $postkey, $postvalue, $idCounter, 0);
}
}
Again, the print statement is for debugging purposes. Additional info: The names of the HTML input fields are dynamically assigned numbers based on their order in the JSON tree. So, incrementing the variable idCounter corresponds to proceeding to the next input field.
Edit3: added in comments to code.You can (and should) allways use PHP's internal function, in case there is any.
If you don't need the counter, you could look at array_replace_recursive. In that case, your code would look like this:
function replaceVal(&$array, $key, $value) {
$array = array_replace_recursive( $array, array( $key => $value ) );
}
EDIT
After current comments:
function replaceVal(&$json, $postkey, $postvalue, &$idCounter, $level)
{
$depth = 3;
if ($level >= $depth){
foreach($json as $key => $value) {
if($idCounter == $postkey) {
$json[$key] = $postvalue; #Not working properly
return;
}
$idCounter++;
}
}
if ($level < $depth){
foreach($json as $key => $value){
if ($idCounter < $postkey)
replaceVal($json[$key], $postkey, $postvalue, $idCounter, $level+1);
else
return;
}
}
}
The problem was that, in the recursion, you where using $value
, wich is a copy of the array element. Then, that was edited, but the changes didn't propagate to $json
.
There is another way to do it. The main idea is to consider JSON as a string and then use str_replace or preg_replace(str_replace for regexp). There is an example:
# Creating a mapping array ("old_key_name" => "new_key_name").
# There I'm reading a mapping from the json file $mapping_json_file.
# But you can set your mapping array directly instead, like $mapping_array = array("old_key_name" => "new_key_name",...).
$mapping_array = json_decode(file_get_contents($mapping_json_file),true);
# Replace string
$new_json = str_replace(array_keys($mapping_array ), array_values($mapping_array), $old_json);
NOTE: it would be better to use full match for string replace. There is a siple way one can do it.
# For each key name, replace each $old_key_name by "/\b".$old_key_name."\b/u".
# It's needed for recognizing breakers.
$tmp_arr = array_map(function($k){ return '/\b'.$k.'\b/u'; }, array_keys($mapping_array));
# Now we use "/\b".$old_key_name."\b/u" instead $old_key_name.
$new_json = preg_replace($tmp_arr, array_values($mapping_array), $old_json);
精彩评论