将表单平展的JSON转换回对象

时间:2010-04-20 16:33:28

标签: javascript json forms arrays object

必需(因此请不要挑选这个要求,我已经挑选了它,这就是req)来转换某些嵌入了“对象嵌套”的表单字段在字段名称中,返回对象本身。以下是一些典型的表单字段名称:

  • phones_0_patientPhoneTypeId
  • phones_0_phone
  • phones_1_patientPhoneTypeId
  • phones_1_phone

上面的表单字段来自一个对象,例如向下的对象(参见“数据”),这是我需要重新组装的对象的格式。可以假设任何名称包含下划线_字符的表单字段都需要进行此转换。下划线之间的表单字段的段(如果是数字)表示Javascript数组,否则表示对象。

我发现很容易设计一个(有点幼稚)实现原始对象的“扁平化”以供表单使用,但我正朝着另一个方向奋斗;在下面的对象/数据下方,我正在粘贴当前的尝试。一个问题(也许是唯一一个?)是它目前没有正确考虑数组索引,但这可能很棘手,因为该对象随后将被编码为JSON,这将不考虑稀疏数组。因此,如果“phones_1”存在,但“phones_0”不存在,我仍然希望确保电话[0]存在一个插槽,即使该值为空。

鼓励调整我已经开始或完全不同的实现。如果有兴趣,请告诉我您是否希望看到我的代码“flattening”部分正在运行。提前致谢

数据:

var obj = {
phones: [{
    "patientPhoneTypeId": 4,
    "phone": "8005551212"
},
{
    "patientPhoneTypeId": 2,
    "phone": "8885551212"
}]};

迄今为止的代码:

    var unflattened = {};

    for (var prop in values) {
        if (prop.indexOf('_') > -1) {
            var lastUnderbarPos = prop.lastIndexOf('_');

            var nestedProp = prop.substr(lastUnderbarPos + 1);
            var nesting = prop.substr(0, lastUnderbarPos).split("_");

            var nestedRef, isArray, isObject;

            for (var i=0, n=nesting.length; i<n; i++) {
                if (i===0) {
                    nestedRef = unflattened;
                }

                if (i < (n-1)) { // not last
                    if (/^\d+$/.test(nesting[i+1])) {
                        isArray = true;
                        isObject = false;
                    }
                    else {
                        isArray = true;
                        isObject = false;
                    }

                    var currProp = nesting[i];

                    if (!nestedRef[currProp]) {
                        if (isArray) {
                            nestedRef[currProp] = [];
                        }
                        else if (isObject) {
                            nestedRef[currProp] = {};
                        }
                    }

                    nestedRef = nestedRef[currProp];
                }
                else {
                    nestedRef[nestedProp] = values[prop];
                }
            }
        }

3 个答案:

答案 0 :(得分:1)

我会将表单字段名称更改为暗示什么是数组更简单,以及使用此结构的对象是什么:

property.<remaining>     => looking at "property" object
property[i].<remaining>  => looking at "property" array with index "i"

使用此信息,在没有太多字符串和索引操作的情况下重新生成数组变得更容易。假设我们有一个空的根对象,你可以为它添加一个名为set的方法,它只是按原样获取属性字符串,并构建对象,无论它有多深的嵌套。

var object = {};

object.set = function(key, value) {
    var parts = key.split(".");
    var current = this; // begin at root
    var arrayRegex = /(\w+)\[(\d+)\]/, nameAndIndex, name, index;

    while(parts.length) {
        part = parts.shift();
        nameAndIndex = part.match(arrayRegex);
        // property is an array
        if(nameAndIndex != null) {
            name = nameAndIndex[1];
            index = nameAndIndex[2];
            current[name] = current[name] || [];
            current[name][index] = current[name][index] || {};
            current = current[name][index];
        }
        // property is an object
        else {
            current[part] = current[part] || {};
            current = current[part]; // edit: was missing this line before
        }
    }

    current[part] = value;
};

这可以处理深层嵌套的结构,但有一点需要注意,你必须在每个级别都有一个对象。例如,您无法执行someProperty[1][2],但可以执行someProperty[1].anotherProperty[2]。此外, last 项必须是对象属性而不是数组元素,这意味着您无法执行x.y.z.lastProperty[1],但您可以执行x.y.z.anArray[1].lastProperty。如果上述情况不起作用,则可以更改set方法并添加其他检查。

使用以下测试用例在http://jsfiddle.net/ysQpF/上创建了一个示例:

object.set('phones[0].phoneType', 1);
object.set('phones[0].phone', '312-223-4929');
object.set('phones[0].details[0].areaCode', '312');
object.set('phones[0].details[0].region[0].name', 'Alaska');
object.set('phones[0].details[0].region[1].name', 'California');
object.set('phones[1].phoneType', 2);
object.set('phones[1].phone', '408-332-3011');

答案 1 :(得分:0)

对于每个表单字段:

EG。 phones_0_patientPhoneTypeId

  1. 在_上拆分名称以获取数组:["phones","0","patientPhoneTypeId"]
  2. reverse()数组:["patientPhoneTypeId","0","phones"]
  3. 将模板对象放入变量中,我将使用o
  4. pop()数组,直到它为空,除了最后一个之外的每一个都做o = o[resultOfPop]
  5. 对于最后一个,只需设置o[resultOfPop] = valueFromField
  6. 等值即可

    如果您没有/想要使用模板对象,则需要在步骤4中添加空对象或数组。

答案 2 :(得分:0)

感谢其他答案。这是我如何调整我已经拥有的代码,我知道我已经接近了。

    var unflattened = {};

    for (var prop in values) {
        if (prop.indexOf('_') > -1) {
            var nesting = prop.split("_");
            var nestedProp = nesting.pop();

            if (!nesting.length || !nestedProp) {
                console.log("form field name: '" + origProp + "' cannot be converted to 'nested' object");
                continue;
            }

            var nestedRef, currProp, isArray, isObject;

            for (var i=0, n=nesting.length; i<n; i++) {                 
                if (i === n-1) { //last
                    isArray = /^\d+$/.test(nestedProp);
                }
                else {
                    isArray = /^\d+$/.test(nesting[i+1]);
                }

                isObject = !isArray;
                currProp = nesting[i];

                if (i===0) {
                    nestedRef = unflattened;
                }

                if (!nestedRef[currProp]) {
                    if (isArray) {
                        nestedRef[currProp] = [];
                    }
                    else {
                        nestedRef[currProp] = {};
                    }
                }

                nestedRef = nestedRef[currProp];

                if (i === n-1) { //last
                    nestedRef[nestedProp] = values[prop];
                }
            }
        }
    }