Convert an HTML form field to a JSON object with inner objects

16,568

Solution 1

I think that what you'd do is this: for each input, first split the name at the separators (the '.' characters). Now, you have an array of names. You can then iterate through that array, making sure that your target "assembly" object (and sub-objects) have containers every time you come across a new name segment. When the array has 1 element in it, you simply add the value.

$.fn.extractObject = function() {
  var accum = {};
  function add(accum, namev, value) {
    if (namev.length == 1)
      accum[namev[0]] = value;
    else {
      if (accum[namev[0]] == null)
        accum[namev[0]] = {};
      add(accum[namev[0]], namev.slice(1), value);
    }
  }; 
  this.find('input, textarea, select').each(function() {
    add(accum, $(this).attr('name').split('.'), $(this).val());
  });
  return accum;
});
// ...

var object = $('#myform').extractObject();

I just sort-of made that up so there might be a bug or two; I can't remember whether all the browsers have "slice" but I think they do.

(edit: I forgot the all-important call to split())

Solution 2

You can loop through the form fields by name, use String#split to split the names on dot, and build up your resulting structure. Concept code:

function serializeDeep(form) {
    var rv, obj, elements, element, index, names, nameIndex, value;

    rv = {};
    elements = form.elements;
    for (index = 0; index < elements.length; ++index) {
        element = elements[index];
        name = element.name;
        if (name) {
            value = $(element).val();
            names = name.split(".");
            obj = rv;
            for (nameIndex = 0; nameIndex < names.length; ++nameIndex) {
                name = names[nameIndex];
                if (nameIndex == names.length - 1) {
                    obj[name] = value;
                }
                else {
                    obj = obj[name] = obj[name] || {};
                }
            }
        }
    }
    return rv;
}

Note that that doesn't allow for fields with repeated names (which should create arrays), nor does it elegantly handle a situation where you use the names "foo" and "foo.bar". But it should get you started.

Solution 3

I have managed it this way:

$('#Myform').attr('onsubmit', 'test()');

function test() {

  var obj = {};

  obj.title =$('#title').prop('value');
  console.log('title: '+obj.title);

  obj.website =$('#website').prop('value');
  console.log('website: '+obj.website);

  obj.tags =$('#tags').prop('value').split(',');
  console.log('tags: '+obj.tags);

  do_something(JSON.stringify(obj));

}

Of course this can be done if you know what the names are, and I am in fact generating the table itself using Formation plug-in.

Solution 4

I created an example for this question by using plain js, please check developer tool console to see the data object!

jsfiddle example

var data = {};
var array = 'person.name.first'.split('.');
var value = 'myFirstName';
generateObj(data, array, value);
console.log(data);

function generateObj(obj, arr, val) {
    if (arr.length === 1) {
        obj[arr[0]] = val
        return;
    } 

    var restArr = arr.splice(1);
    if (!obj[arr[0]]) {
        obj[arr[0]] = {};
    }
    generateObj(obj[arr[0]], restArr, val);
}

solution:

  1. transform each name string to array.
  2. iterate through each array.
  3. recursively call a method which create an obj and set this obj as the value of the property and pass this obj to the next recursion.
Share:
16,568

Related videos on Youtube

Tawani
Author by

Tawani

Updated on April 17, 2022

Comments

  • Tawani
    Tawani about 2 years

    Given the following HTML form:

    <form id="myform">
      Company: <input type="text" name="Company" value="ACME, INC."/>
      First Name: <input type="text" name="Contact.FirstName" value="Daffy"/>
      Last Name: <input type="text" name="Contact.LastName" value="Duck"/>
    </form>
    

    What is the best way serialize this form in javascript to a JSON object in the format:

    {
      Company:"ACME, INC.",
      Contact:{FirstName:"Daffy", LastName:"Duck"}
    }
    

    Also note that there might be more than 1 "." sign in the field name.

    • Roman
      Roman about 14 years
      @Tawani: Do you mind me asking why you're serializing it to JSON instead of just submitting it and figuring it out server side?
    • Tawani
      Tawani about 14 years
      @R0MANARMY: The submitted to a REST web service that exists on another webserver.
  • Pointy
    Pointy about 14 years
    I don't think that's what the question is about.
  • T.J. Crowder
    T.J. Crowder about 14 years
    "What's your take then?" How to do part 1 of your answer.
  • Pointy
    Pointy about 14 years
    I just typed in an answer - he wants the dotted names to imply structure.
  • Tawani
    Tawani about 14 years
    Exactly what I needed. I also added checks for buttons, multiple checkboxes (with same name) and elements without names.
  • Ronan
    Ronan over 12 years
    quick'n dirty trick to handle checkbox input, to add just before "add(accum, $(this).attr(..." : if( $(this).attr('checked') && ('checked' == $(this).attr('checked'))) { $(this).val(true); }