views:

440

answers:

4

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.

A: 

Create an object of that shape then use a JSON encoder to write it out.

spender
I don't think that's what the question is about.
Pointy
What's your take then?
spender
*"What's your take then?"* How to do part 1 of your answer.
T.J. Crowder
@spender I just typed in an answer - he wants the dotted names to imply structure.
Pointy
A: 
var data = $('#myform').serializeArray():
data = JSON.stringify(data);

should do the trick, if the JSON object is available in your browser, or was added by script. check if(window.JSON) probably.

Kind Regards

--Andy

jAndy
That does not do what he asked. Give the question another read.
T.J. Crowder
ou woah, your're right. *shame*
jAndy
Easily done, we all skim a bit sometimes. (You have the option of deleting your answer, btw.)
T.J. Crowder
+4  A: 

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())

Pointy
Exactly what I needed. I also added checks for buttons, multiple checkboxes (with same name) and elements without names.
Tawani
+1  A: 

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.

T.J. Crowder