简体   繁体   中英

PHP Recursive JSON Children Search

I need to parse JSON which looks like this:

{
    "mdfId":"282088127",
    "mdfConcept":"ME 3400EG-12CS-M Switch",
    "children":[
        {
            "mdfId":"007",
            "mdfConcept":"Another item",
            "children": [
                // many more here
            ]
        },
        {
             "mdfId":"008",
             "mdfConcept":"Another one",
             "children": [
                 {
                     "mdfId":"010",
                     "mdfConcept":"What I'm looking for!",
                     "children": [] // no children
                 }
             ]
        },
        // many more here
    ]
},

This is a recursive structure in which every element has mdfId , mdfConcept and children keys.

Say I need to find node with ID=010 within this structure. I don't know at which level it lies (eg it can be on top level, or several children nodes below).

My current approach is:

$mdfId = '010'; // what I'm loking for

foreach ($jsonResponse as $category) {
    while (true) {
        if ($category['mdfId'] == $mdfId) {
            // we found it!
            $categoryDevices[$mdfId] = $category['children'];
            break 2;
        }

        if (!empty($category['children'])) {
            next_cat:

            if (is_null($category['children'])) {
                break;
            }

            $category = array_shift($category['children']);
            continue;
        }

        if (empty($category['children'])) {
            goto next_cat;
        }
    }
}

But current approach misses some cases. How can I optimize this recursive loop so it checks all nodes on same level and each one accesible through any number of children keys?

An embarrassing feature of your JSON object is that, while each children member is an array of the "child" structure, the top level one is the object itself , so it's an obstacle to a really recursive approach.

We might workaround by turning the source JSON object into the same structure as nested levels, ie:

  • having $jsonResponse as original object
  • use ['children' => $jsonResponse] instead

This way, it should work with something like this:

$mdfId = '010'; // what I'm loking for

if ($result = look4id(['children' => $jsonResponse], $mdfId) {
    $categoryDevices[$mdfId] = $result;
}

function look4id($source, $id) {
    foreach ($source as $child) {
        if ($child['mdfId'] == $id) {
            return $source['children'];
        } else {
            if ($source['children']) {
                return look4id($source['children'], $id);
            }
        }
    }
}

So basically I wrote a function that didn't return anything, but rather populated a variable from arguments.

function findRecursiveArrayNodeById($id, $array, &$node) {
    foreach ($array as $child) {
        if (isset($child['mdfId']) && $child['mdfId'] == $id) {
            $node = $child;
            return;
        }

        if (!empty($child['children'])) {
            findRecursiveArrayNodeById($id, $child['children'], $node);
        }
    }
}

Usage as follows:

$result = false;

findRecursiveArrayNodeById($mdfId, $category_json, $result);

if (!$result) {
    println("did not find {$mdfId}");
    continue;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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