在处理嵌套JSON数据时避免嵌套each循环

Avoiding nested each loops when handling nested JSON data

本文关键字:嵌套 each 循环 数据 处理 JSON      更新时间:2023-09-26

有一个JSON对象:

{
   "custom_sql_rule":[
      {
         "custom_sql":[
            "Should start with a Select",
            "Should not contain ;",
            "Some other error"
         ],
         "something_else":[
            "Error",
            "Should not contain ;",
            "Some other error"
         ]
      }
   ],
   "someother_rule":[
      {
         "sdfsdf":[
            "Should start with a Select",
            "Should not contain ;",
            "Some other error"
         ],
         "sdfsdf":[
            "Error",
            "Should not contain ;",
            "Some other error"
         ]
      }
   ]
}

我需要将每个字符串错误附加到一个div中。我很快就完成了:

var errorMessages = function(errors, errorsContainer) {
  $.each(errors, function(index, value) {
    $.each(value, function(i, v) {
      $.each(v, function(ia, va) {
        $.each(va, function(iab, vab) {
          errorsContainer.append($("<div></div>").addClass(index).text(vab));
        })
      })
    })
  });
};

这是可怕的。是否有更好的方式来处理这样形成的JSON ?注意,我不能真正使用键名

这个解决方案将遍历一个由对象和数组组成的树,识别每个字符串。

function eachLeafString(dataObject, callbackMethod) {
    if(typeof dataObject == 'string') {
        callbackMethod(dataObject);
    }
    else if (Object.prototype.toString.call( dataObject ) === '[object Array]') {
        $.each(dataObject, function(index, elem) { eachLeafString(elem, callbackMethod); });
    }
    else {
        for(var propertyName in dataObject) {
            eachLeafString(dataObject[propertyName], callbackMethod);
        }
    }
}

这个工作的例子:http://jsfiddle.net/e6XN7/3/

如果您想使用第一级节点的索引,您可以这样使用它:

  $.each(errors, function(index, value) {
    eachLeafString(value, function(vab) {
         errorsContainer.append($("<div></div>").addClass(index).text(vab));
    });
  });

JSON "只是" javascript对象字面值。你可以跳过jQuery调用,直接在parse之后引用对象和数组。

obj.custom_sql_rule[0].something_else[0];
当然,您的JSON格式有点奇怪。如果可能的话,不要任意混合对象{}和数组[]。当您拥有相同对象或原始数据的列表时使用后者,当您拥有命名属性时使用前者。一旦你有了一个有效的javascript对象,你就可以遍历它的属性。
for(var sqlProp in jsonObj) {
  if(jsonObj[sqlProp].error) {
    errorsContainer.append("<div>" + jsonObj[sqlProp].error + "</div>");
  }
}

试试这样的递归函数:

function errorMessages(errors,errorsContainer,errorclass)
{
  if ( typeof errors == "string")
    errorsContainer.append($("<div></div>").addClass(errorclass).text(errors));
  else
  {
    $.each(errors, function(classname, value)
          {
           errorMessages(value,errorsContainer,(typeof errorclass == "undefined") ? classname : errorclass );                           
      });
   }
};

在jsfiddle上检查

当你调用你的函数时,不要传递第三个参数,他将是空的,所以你的类将从你的JSON中获取。如果在参数中传递一个类,它将覆盖JSON中的类。你选择:)

但是你可以修改和优化它…

与其修改JSON中运行的代码,不如修改JSON本身的结构,使其"更宽/更平"而不是"更深"。

//errors
[
  {
    "error" : "custom_sql_error",
    "subcategory" : "custom_sql",
    "messages" : [
      "Should start with a Select",
      "Should not contain ;",
      "Some other error"
    ]
  },
  {
    "error" : "custom_sql_error",
    "subcategory" : "something_else",
    "messages" : [
      "Error",
      "Should not contain ;",
      "Some other error"
    ]
  },
  {
    "error" : "someother_rule",
    "subcategory" : "sdfsdf",
    "messages" : [
      "Should start with a Select",
      "Should not contain ;",
      "Some other error"
    ]
  },
  {
    "error" : "someother_rule",
    "subcategory" : "sdfsdf",
    "messages" : [
      "Error",
      "Should not contain ;",
      "Some other error"
    ]
  }
]

这样,你就像这样循环了

$.each(errors, function(index, value) {
  $.each(value.messages, function (index, message){
    $("<div/>").addClass(index).text(message).appendTo(errorsContainer);
  });
});