我正在尝试从JSON文件中递归加载Scenegraph结构。 我的想法是在所有子节点上调用相同的函数,直到最后一个节点没有子节点为止。但是,在调试代码时,我发现第二个循环内的loadNodes递归调用被忽略了,相反,该循环只是增加了计数器并从下一个循环开始。
我检查了语法,并检查了在循环内调用其他函数的情况(有效)。
有人知道我在做什么错吗?
function loadNodes(obj, current_group_node) {
for (let i = 0; i < obj.length; i++) {
if (obj[i].nodetype === 'group') {
let new_group_node = new GroupNode(new Matrix(obj[i].matrix));
current_group_node.add(new_group_node);
for (let j = 0; j < obj[i].children.length; j++) {
loadNodes(obj[i].children[j], new_group_node);
}
} else {
// some more logic
}
}
}
我的函数接收具有以下可能结构的对象数组:
{
"nodetype": "group",
"name": "root",
"matrix": [
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
],
"children": [
{
"nodetype": "group",
"name": "driver-cube-group",
"matrix": [
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
],
"children": [
{
"nodetype": "aabox",
"name": "driver-cube",
"minpoint": {
"x": -3,
"y": -3,
"z": -3,
"w": 1
},
"maxpoint": {
"x": -2,
"y": -2,
"z": -2,
"w": 1
},
"color": {
"x": 1,
"y": 1,
"z": 1,
"w": 1
}
}
]
}
]
}
答案 0 :(得分:2)
loadNodes
期望第一个参数是对象数组,而不是单个对象。您无需在每个子对象上循环调用它,只需调用一次,并传递child
数组作为参数即可。它在数组上做自己的循环。
所以替换掉它:
for (let j = 0; j < obj[i].children.length; j++) {
loadNodes(obj[i].children[j], new_group_node);
}
具有:
loadNodes(obj[i].children, new_group_node);
将第一个参数从obj
重命名为arr
可能会有所帮助,以明确表示它期望一个数组,而不是单个对象。
答案 1 :(得分:0)
提供JSON后将其传递给您的函数,我想出了以下解决方案(我更改了一些命名法,还通过创建一个对象来替换了两个实例化,为嵌套对象分配一个数组):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en">
<head>
<title>JSON Test Case</title>
<script type="application/javascript">
/* <![CDATA[ */
'use strict';
var nested = 0;
function loadNodes(p_obj, p_current_group_node)
{
let l_i;
let l_new_group_node;
nested++;
console.log('Entering loadNodes (' + nested + ')...');
console.log('Length of object: ' + p_obj.length); // Oops...!
console.log('Type of JSON object: ' + typeof p_obj);
console.log('Determining the node type: ' + p_obj.nodetype);
console.log('Node name: ' + p_obj.name);
if(p_obj.nodetype == 'group')
{
console.log('Number of elements: ' + p_obj.children.length);
for(l_i = 0; l_i < p_obj.children.length; l_i++)
{
console.log('Found a subtree for processing!');
l_new_group_node = new Object();
l_new_group_node.nodes = new Array();
p_current_group_node.nodes.push(l_new_group_node);
loadNodes(p_obj.children[l_i], l_new_group_node);
}
}
else
console.log('Process leaf node here...');
console.log('Leaving loadNodes (' + nested + ')...');
nested--;
}
/* ]]> */
</script>
</head>
<body>
<header><h1>JSON test case</h1></header>
<main>
<script type="application/javascript">
/* <![CDATA[ */
var json = {
"nodetype": "group",
"name": "root",
"matrix": [
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
],
"children": [
{
"nodetype": "group",
"name": "driver-cube-group",
"matrix": [
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
],
"children": [
{
"nodetype": "aabox",
"name": "driver-cube",
"minpoint": {
"x": -3,
"y": -3,
"z": -3,
"w": 1
},
"maxpoint": {
"x": -2,
"y": -2,
"z": -2,
"w": 1
},
"color": {
"x": 1,
"y": 1,
"z": 1,
"w": 1
}
}
]
}
]
}
var myobj = new Object();
myobj.nodes = new Array();
loadNodes(json, myobj);
/* ]]> */
</script>
</main>
</body>
</html>
首先,我可以抛出外部循环,因为它甚至不会触发,因此可以防止递归(我发现p_obj.length
甚至会返回undefined
,如果您试图访问此属性)。毕竟,它是一个对象,并且显然没有提供长度。相反,我要检查已收到的对象的参数,然后确定是否需要进一步下降或实际到达叶节点。
如果需要进一步下降,则实际上有一个循环可以循环浏览分配给属性children
的数组-该数组实际上有一个长度,因此可以将其用作终止条件。
如果要查看实际情况,可以在浏览器控制台打开的情况下运行此测试用例。我插入了各种记录特定信息的语句,以便您可以在此处查看正在执行的操作。
您还应该注意条目 Entering loadNodes(...)和 Leaveing loadNodes(...),因为它们会告诉您实际在何处进行递归以及何时进行递归剩下的递归调用函数。您可以在括号中找到嵌套级别。