具有JSON对象的层次化字段的HTML表单

时间:2021-03-01 15:56:53

For example. We have this form:

为例。我们有这种形式:

<form id="to-object">
<input name="data[zero_key]" value="It`s too simple" />
<input name="data[first_key][value]" value="It`s simple too" />
<input name="data[second_key][]" value="Push me" />
<input name="data[second_key][]" value="Push me please" />
<input name="data[next_key][0][type]" value="I don`t wanna be object with key 0. " />
<input name="data[next_key][0][number]" value="I`m array!!!" />
</form>

So the question is: How to serialize this form correctly and get this result:

所以问题是:如何正确地序列化这个表单并得到这个结果:

    {
  "zero_key": "It`s too simple",
  "first_key": {
    "value": "It`s simple too"
  },
  "second_key": [
    "Push me",
    "Push me please",
  ],
  "next_key": [
    {
      "type": "I don`t wanna be object with key 0.",
      "number": "I`m array!!!"
    },

  ],

}

What do I have:

我有什么:

(function($) {
    $.fn.convertFormDataToObject = function(){

        var extractFieldNames = function(fieldName, expression, keepFirstElement)
        {

            expression = expression || /([^\]\[]+)/g;
            keepFirstElement = keepFirstElement || false;

            var elements = [];

            while((searchResult = expression.exec(fieldName)))
            {
                    elements.push(searchResult[0]);

            }

            if (!keepFirstElement && elements.length > 0) elements.shift();

            return elements;
        }

        var attachProperties = function(target, properties, value)
        {

            var currentTarget = target;
            var propertiesNum = properties.length;
            var lastIndex = propertiesNum - 1;
            for (var i = 0; i < propertiesNum; ++i)

            {
                currentProperty = properties[i];

                if(currentProperty == ""){
                    var intKey = Math.floor(Math.random() * (99 - 1)) + 1;
                    currentProperty = intKey.toString();
                } else if(!isNaN(currentProperty)) {
                    currentProperty = [parseInt(currentProperty)];
                } else {
                    currentProperty = properties[i];
                }

                if (currentTarget[currentProperty] === undefined)
                {
                    currentTarget[currentProperty] = (i === lastIndex) ? value : {};                    }
                                    currentTarget = currentTarget[currentProperty];
            }
        }

        var convertFormDataToObject = function(form) {
                var currentField = null;
            var currentProperties = null;
                // result of this function
            var data = {};
            // get array of fields that exist in this form
            var fields = form.serializeArray();
            for (var i = 0; i < fields.length; ++i)
            { currentField = fields[i];
                // extract field names
                currentProperties = extractFieldNames(currentField.name);
                // add new fields to our data object
                attachProperties(data, currentProperties, currentField.value);
            }
            return data;
        }
        var form = $(this);
        var data = convertFormDataToObject(form);
        return data;
    };
})(jQuery);

As you can see in this sample I'm using random int generator to fix issue with empty space between []. That's a problem but not crucial. Crucial thing is that I'm not able to fix issue with arrays generating. This script adds all keys from name attribute as key of object and it gives wrong object. Instead of array with objects it gives object with keys "0","1" etc. For example "0":{},"1":{} instead of [{},{}].

正如您在这个示例中看到的,我正在使用随机int生成器来修复[]之间的空空间问题。这是个问题,但不是关键。关键是我不能修复数组生成的问题。此脚本将name属性中的所有键添加为object的键,并给出错误的对象。它不使用对象数组,而是使用键“0”、“1”等给对象,例如“0”:{},“1”:{}而不是[{},{}]。

If you know how to fix it with JS I'll very appreciate your help!

如果您知道如何用JS修复它,我将非常感谢您的帮助!

1 个解决方案

#1


1  

You can reduce the complexity of your code by quite a lot, try something like this:

您可以通过大量的方法来减少代码的复杂性:

const finalObj = [...document.querySelector('form').children].reduce((objSoFar, child) => {
  const value = child.value;
  let allKeys = child.name.slice(4).split('][');
  allKeys[0] = allKeys[0].slice(1);
  const lastKeyIndex = allKeys.length - 1;
  allKeys[lastKeyIndex] = allKeys[lastKeyIndex].slice(0, allKeys[lastKeyIndex].length - 1);
  // now: eg "data[next_key][0][number]" has allKeys ["next_key", "0", "number"]
  
  let refObj = objSoFar;
  while (allKeys.length > 1){
    if (!refObj[allKeys[0]]) {
      if (allKeys[1] === '' || /^\d+$/.test(allKeys[1])) refObj[allKeys[0]] = [];
      else refObj[allKeys[0]] = {};
    }
    refObj = refObj[allKeys[0]];
    allKeys = allKeys.slice(1);
  }
  
  const lastKey = allKeys[0];
  if (lastKey === '') refObj.push(value);
  else refObj[lastKey] = value;
  return objSoFar;
}, {});
console.log(finalObj);

const desiredSerializedResult = '{"zero_key":"It`s too simple","first_key":{"value":"It`s simple too"},"second_key":["Push me","Push me please"],"next_key":[{"type":"I don`t wanna be object with key 0.","number":"I`m array!!!"}]}';
if (JSON.stringify(finalObj) === desiredSerializedResult) console.log('Matches desiredSerializedResult');
<form id="to-object">
  <input name="data[zero_key]" value="It`s too simple" />
  <input name="data[first_key][value]" value="It`s simple too" />
  <input name="data[second_key][]" value="Push me" />
  <input name="data[second_key][]" value="Push me please" />
  <input name="data[next_key][0][type]" value="I don`t wanna be object with key 0." />
  <input name="data[next_key][0][number]" value="I`m array!!!" />
</form>

(But this really seems like an XY problem - there's almost certainly a better way to set up your application than something that requires logic like this)

(但这确实是一个XY问题——几乎可以肯定的是,建立应用程序的方法要比需要这样逻辑的方法更好)

#1


1  

You can reduce the complexity of your code by quite a lot, try something like this:

您可以通过大量的方法来减少代码的复杂性:

const finalObj = [...document.querySelector('form').children].reduce((objSoFar, child) => {
  const value = child.value;
  let allKeys = child.name.slice(4).split('][');
  allKeys[0] = allKeys[0].slice(1);
  const lastKeyIndex = allKeys.length - 1;
  allKeys[lastKeyIndex] = allKeys[lastKeyIndex].slice(0, allKeys[lastKeyIndex].length - 1);
  // now: eg "data[next_key][0][number]" has allKeys ["next_key", "0", "number"]
  
  let refObj = objSoFar;
  while (allKeys.length > 1){
    if (!refObj[allKeys[0]]) {
      if (allKeys[1] === '' || /^\d+$/.test(allKeys[1])) refObj[allKeys[0]] = [];
      else refObj[allKeys[0]] = {};
    }
    refObj = refObj[allKeys[0]];
    allKeys = allKeys.slice(1);
  }
  
  const lastKey = allKeys[0];
  if (lastKey === '') refObj.push(value);
  else refObj[lastKey] = value;
  return objSoFar;
}, {});
console.log(finalObj);

const desiredSerializedResult = '{"zero_key":"It`s too simple","first_key":{"value":"It`s simple too"},"second_key":["Push me","Push me please"],"next_key":[{"type":"I don`t wanna be object with key 0.","number":"I`m array!!!"}]}';
if (JSON.stringify(finalObj) === desiredSerializedResult) console.log('Matches desiredSerializedResult');
<form id="to-object">
  <input name="data[zero_key]" value="It`s too simple" />
  <input name="data[first_key][value]" value="It`s simple too" />
  <input name="data[second_key][]" value="Push me" />
  <input name="data[second_key][]" value="Push me please" />
  <input name="data[next_key][0][type]" value="I don`t wanna be object with key 0." />
  <input name="data[next_key][0][number]" value="I`m array!!!" />
</form>

(But this really seems like an XY problem - there's almost certainly a better way to set up your application than something that requires logic like this)

(但这确实是一个XY问题——几乎可以肯定的是,建立应用程序的方法要比需要这样逻辑的方法更好)