How to Serialize Html Form to JSON
This article will guide you on how to use serialize JSON jquery to serialize Form Data and post it
HTML form
JSON Output
jquery.serializeJSON
Adds the method .serializeJSON()
to jQuery (or Zepto) that serializes a form into a JavaScript Object, using the same format as the default Ruby on Rails request params.
How to use it
To use serializeJSON just download the jquery.serializejson.js script. And make sure it is included after jQuery, for example:
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.serializejson.js"></script>
Usage Example
HTML form:
<form>
<input type="text" name="title" value="Finding Loot"/>
<input type="text" name="author[name]" value="John Smith"/>
<input type="text" name="author[job]" value="Legendary Pirate"/>
</form>
JavaScript:
$('form').serializeJSON();// returns =>
{
title: "Finding Loot",
author: {
name: "John Smith",
job: "Legendary Pirate"
}
}
Form input, textarea and select tags are supported. Nested attributes and arrays can be specified by using the attr[nested][nested]
syntax.
HTML form:
<form id="my-profile">
<!-- simple attribute -->
<input type="text" name="fullName" value="Mario Izquierdo" /> <!-- nested attributes -->
<input type="text" name="address[city]" value="San Francisco" />
<input type="text" name="address[state][name]" value="California" />
<input type="text" name="address[state][abbr]" value="CA" /> <!-- array -->
<input type="text" name="jobbies[]" value="code" />
<input type="text" name="jobbies[]" value="climbing" /> <!-- nested arrays, textareas, checkboxes ... -->
<textarea name="projects[0][name]">serializeJSON</textarea>
<textarea name="projects[0][language]">javascript</textarea>
<input type="hidden" name="projects[0][popular]" value="0" />
<input type="checkbox" name="projects[0][popular]" value="1" checked /> <textarea name="projects[1][name]">tinytest.js</textarea>
<textarea name="projects[1][language]">javascript</textarea>
<input type="hidden" name="projects[1][popular]" value="0" />
<input type="checkbox" name="projects[1][popular]" value="1"/> <!-- select -->
<select name="selectOne">
<option value="paper">Paper</option>
<option value="rock" selected>Rock</option>
<option value="scissors">Scissors</option>
</select> <!-- select multiple options, just name it as an array[] -->
<select multiple name="selectMultiple[]">
<option value="red" selected>Red</option>
<option value="blue" selected>Blue</option>
<option value="yellow">Yellow</option>
</select>
</form>
JavaScript:
$('#my-profile').serializeJSON();// returns =>
{
fullName: "Mario Izquierdo", address: {
city: "San Francisco",
state: {
name: "California",
abbr: "CA"
}
}, jobbies: ["code", "climbing"], projects: {
'0': { name: "serializeJSON", language: "javascript", popular: "1" },
'1': { name: "tinytest.js", language: "javascript", popular: "0" }
}, selectOne: "rock",
selectMultiple: ["red", "blue"]
}
The serializeJSON
function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject
or similar, but those plugins already existed.
To convert into a JSON String, use the JSON.stringify
method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).
var obj = $('form').serializeJSON();
var jsonString = JSON.stringify(obj);
The plugin implementation relies on jQuery’s .serializeArray() method. This means that it only serializes the inputs supported by .serializeArray()
, which follows the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.
Parse values with types
All attribute values are strings by default. But you can force values to be parsed with specific types by appending the type with a colon.
<form>
<input type="text" name="strbydefault" value=":string is the default (implicit) type"/>
<input type="text" name="text:string" value=":string type can still be used to overrid other parsing options"/>
<input type="text" name="excluded:skip" value="Use :skip to not include this field in the result"/> <input type="text" name="numbers[1]:number" value="1"/>
<input type="text" name="numbers[1.1]:number" value="1.1"/>
<input type="text" name="numbers[other stuff]:number" value="other stuff"/> <input type="text" name="bools[true]:boolean" value="true"/>
<input type="text" name="bools[false]:boolean" value="false"/>
<input type="text" name="bools[0]:boolean" value="0"/> <input type="text" name="nulls[null]:null" value="null"/>
<input type="text" name="nulls[other stuff]:null" value="other stuff"/> <input type="text" name="autos[string]:auto" value="text with stuff"/>
<input type="text" name="autos[0]:auto" value="0"/>
<input type="text" name="autos[1]:auto" value="1"/>
<input type="text" name="autos[true]:auto" value="true"/>
<input type="text" name="autos[false]:auto" value="false"/>
<input type="text" name="autos[null]:auto" value="null"/>
<input type="text" name="autos[list]:auto" value="[1, 2, 3]"/> <input type="text" name="arrays[empty]:array" value="[]"/>
<input type="text" name="arrays[list]:array" value="[1, 2, 3]"/> <input type="text" name="objects[empty]:object" value="{}"/>
<input type="text" name="objects[dict]:object" value='{"my": "stuff"}'/>
</form>$('form').serializeJSON();// returns =>
{
"strbydefault": ":string is the default (implicit) type",
"text": ":string type can still be used to overrid other parsing options",
// excluded:skip is not included in the output
"numbers": {
"1": 1,
"1.1": 1.1,
"other stuff": NaN, // <-- Not a Number
},
"bools": {
"true": true,
"false": false,
"0": false, // <-- "false", "null", "undefined", "", "0" parse as false
},
"nulls": {
"null": null, // <-- "false", "null", "undefined", "", "0" parse as null
"other stuff": "other stuff"
},
"autos": { // <-- works like the parseAll option
"string": "text with stuff",
"0": 0, // <-- parsed as number
"1": 1, // <-- parsed as number
"true": true, // <-- parsed as boolean
"false": false, // <-- parsed as boolean
"null": null, // <-- parsed as null
"list": "[1, 2, 3]" // <-- array and object types are not auto-parsed
},
"arrays": { // <-- uses JSON.parse
"empty": [],
"not empty": [1,2,3]
},
"objects": { // <-- uses JSON.parse
"empty": {},
"not empty": {"my": "stuff"}
}
}
Types can also be specified with the attribute data-value-type
, instead of having to add the ": type" suffix:
<form>
<input type="text" name="anumb" data-value-type="number" value="1"/>
<input type="text" name="abool" data-value-type="boolean" value="true"/>
<input type="text" name="anull" data-value-type="null" value="null"/>
<input type="text" name="anauto" data-value-type="auto" value="0"/>
</form>
Options
By default .serializeJSON()
with no options has this behavior:
- Values are always strings (unless appending :types to the input names)
- Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
- Disabled elements are ignored (W3C rules)
- Keys (input names) are always strings (nested params are objects by default)
This is because serializeJSON
is designed to return exactly the same as a regular HTML form submission when serialized as Rack/Rails params, which ensures maximum compatibility and stability.
Allowed options to change the default behavior:
- checkboxUncheckedValue: string, string value used on unchecked checkboxes (otherwise those values are ignored). For example
{checkboxUncheckedValue: ""}
. If the value needs to be parsed (i.e. to a Boolean or Null) use a parse option (i.e.parseBooleans: true
) or define the input with the:boolean
or:null
types. - parseBooleans: true, automatically detect and convert strings
"true"
and"false"
to booleanstrue
andfalse
. - parseNumbers: true, automatically detect and convert strings like
"1"
,"33.33"
,"-44"
to numbers like1
,33.33
,-44
. - parseNulls: true, automatically detect and convert the string
"null"
to the null valuenull
. - parseAll: true, all of the above. This is the same as if the default :type was
:auto
instead of:string
. - parseWithFunction: function, define your own parse
function(inputValue, inputName) { return parsedValue }
. - skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use
data-skip-falsy="true"
input attribute as well. Falsy values are determined after converting to a given type, note that"0"
as :string is truthy, but0
as :number is falsy. - skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e.
skipFalsyValuesForTypes: ["string", "number"]
would skip""
for:string
fields, and0
for:number
fields). - customTypes: {}, define your own :types or override the default types. Defined as an object like
{ type: function(value){...} }
. For example:{customTypes: {nullable: function(str){ return str || null; }}
. - defaultTypes: {defaultTypes}, in case you want to re-define all the :types. Defined as an object like
{ type: function(value){...} }
- useIntKeysAsArrayIndex: true, when using integers as keys (i.e.
<input name="foods[0]" value="banana">
), serialize as an array ({"foods": ["banana"]}
) instead of an object ({"foods": {"0": "banana"}
).
More info about options usage in the sections below.
Include unchecked checkboxes
In my opinion, the most confusing detail when serializing a form is the input type checkbox, that will include the value if checked, and nothing if unchecked.
To deal with this, it is a common practice to use hidden fields for the “unchecked” values:
<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not -->
<input type="hidden" name="booleanAttr" value="false" />
<input type="checkbox" name="booleanAttr" value="true" />
This solution is somehow verbose, but it is unobtrusive and ensures progressive enhancement, because it is the standard HTML behavior (also works without JavaScript).
But, to make things easier, serializeJSON
includes the option checkboxUncheckedValue
and the possibility to add the attribute data-unchecked-value
to the checkboxes.
For example:
<form>
<input type="checkbox" name="check1" value="true" checked/>
<input type="checkbox" name="check2" value="true"/>
<input type="checkbox" name="check3" value="true"/>
</form>
Serializes like this by default:
$('form').serializeJSON();// returns =>
{'check1': 'true'} // Note that check2 and check3 are not included because they are not checked
Which ignores any unchecked checkboxes. To include all checkboxes, use the checkboxUncheckedValue
option like this:
$('form').serializeJSON({checkboxUncheckedValue: "false"});// returns =>
{'check1': 'true', check2: 'false', check3: 'false'}
The “unchecked” value can also be specified via the HTML attribute data-unchecked-value
(Note this attribute is only recognized by the plugin):
<form id="checkboxes">
<input type="checkbox" name="checked[bool]" value="true" data-unchecked-value="false" checked/>
<input type="checkbox" name="checked[bin]" value="1" data-unchecked-value="0" checked/>
<input type="checkbox" name="checked[cool]" value="YUP" checked/> <input type="checkbox" name="unchecked[bool]" value="true" data-unchecked-value="false" />
<input type="checkbox" name="unchecked[bin]" value="1" data-unchecked-value="0" />
<input type="checkbox" name="unchecked[cool]" value="YUP" /> <!-- No unchecked value specified -->
</form>
Serializes like this by default:
$('form#checkboxes').serializeJSON(); // Note no option is used// returns =>
{
'checked': {
'bool': 'true',
'bin': '1',
'cool': 'YUP'
},
'unchecked': {
'bool': 'false',
'bin': '0'
// Note that unchecked cool does not appear, because it doesn't use data-unchecked-value
}
}
You can use both the option checkboxUncheckedValue
and the attribute data-unchecked-value
at the same time, in which case the attribute has precedence over the option. And remember that you can combine it with other options to parse values as well.
$('form#checkboxes').serializeJSON({checkboxUncheckedValue: 'NOPE', parseBooleans: true, parseNumbers: true});// returns =>
{
'checked': {
'bool': true,
'bin': 1,
'cool': 'YUP'
},
'unchecked': {
'bool': false, // value from data-unchecked-value attribute, and parsed with parseBooleans
'bin': 0, // value from data-unchecked-value attribute, and parsed with parseNumbers
'cool': 'NOPE' // value from checkboxUncheckedValue option
}
}
Automatically Detect Types With Parse Options
The default type is :string, so all values are Strings by default, even if they look like booleans, numbers or nulls. For example:
<form>
<input type="text" name="bool[true]" value="true"/>
<input type="text" name="bool[false]" value="false"/>
<input type="text" name="number[0]" value="0"/>
<input type="text" name="number[1]" value="1"/>
<input type="text" name="number[2.2]" value="2.2"/>
<input type="text" name="number[-2.25]" value="-2.25"/>
<input type="text" name="null" value="null"/>
<input type="text" name="string" value="text is always string"/>
<input type="text" name="empty" value=""/>
</form>$('form').serializeJSON();// returns =>
{
"bool": {
"true": "true",
"false": "false",
}
"number": {
"0": "0",
"1": "1",
"2.2": "2.2",
"-2.25": "-2.25",
}
"null": "null",
"string": "text is always string",
"empty": ""
}
Note that all values are strings.
To auto-detect types, you could use the :auto type (append :auto to input name). Or, you could use the parse options. For example, to parse nulls and numbers:
$('form').serializeJSON({parseNulls: true, parseNumbers: true});// returns =>
{
"bool": {
"true": "true", // booleans are still strings, because parseBooleans was not set
"false": "false",
}
"number": {
"0": 0, // numbers are parsed because parseNumbers: true
"1": 1,
"2.2": 2.2,
"-2.25": -2.25,
}
"null": null, // "null" strings are converted to null becase parseNulls: true
"string": "text is always string",
"empty": ""
}
For rare cases, a custom parser can be defined with a function:
var emptyStringsAndZerosToNulls = function(val, inputName) {
if (val === "") return null; // parse empty strings as nulls
if (val === 0) return null; // parse 0 as null
return val;
}$('form').serializeJSON({parseWithFunction: emptyStringsAndZerosToNulls, parseNumbers: true});// returns =>
{
"bool": {
"true": "true",
"false": "false",
}
"number": {
"0": null, // <-- parsed with custom function
"1": 1,
"2.2": 2.2,
"-2.25": -2.25,
}
"null": "null",
"string": "text is always string",
"empty": null // <-- parsed with custom function
}
Custom Types
You can define your own types or override the defaults with the customTypes
option. For example:
<form>
<input type="text" name="scary:alwaysBoo" value="not boo"/>
<input type="text" name="str:string" value="str"/>
<input type="text" name="number:number" value="5"/>
</form>$('form').serializeJSON({
customTypes: {
alwaysBoo: function(str) { // value is always a string
return "boo";
},
string: function(str) { // all strings will now end with " override"
return str + " override";
}
}
});// returns =>
{
"scary": "boo", // <-- parsed with type :alwaysBoo
"str": "str override", // <-- parsed with new type :string (instead of the default)
"number": 5, // <-- the default :number still works
}
The default types are defined in $.serializeJSON.defaultOptions.defaultTypes
. If you want to define your own set of types, you could also re-define that option (it will not override the types, but define a new set of types).
Ignore Empty Form Fields
You can use the option .serializeJSON(skipFalsyValuesForTypes: ["string"])
, which ignores any string field with an empty value (default type is :string, and empty strings are falsy).
Another option, since serializeJSON()
is called on a jQuery object, is to just use the proper jQuery selector to skip empty values (see Issue #28 for more info):
// Select only imputs that have a non-empty value
$('form :input[value!=""]').serializeJSON();// Or filter them from the form
obj = $('form').find('input').not('[value=""]').serializeJSON();// For more complicated filtering, you can use a function
obj = $form.find(':input').filter(function () {
return $.trim(this.value).length > 0
}).serializeJSON();
Ignore Fields With Falsy Values
When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN
) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"]
or skipFalsyValuesForTypes: ["string", "null"]
.
Or setting a data attribute data-skip-falsy="true"
on the inputs that should be ignored. Note that data-skip-falsy
is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true">
(Note that "0"
as a string is not falsy, but 0
as number is falsy)).
Use integer keys as array indexes
By default, all serialized keys are strings, this includes keys that look like numbers like this:
<form>
<input type="text" name="arr[0]" value="foo"/>
<input type="text" name="arr[1]" value="var"/>
<input type="text" name="arr[5]" value="inn"/>
</form>$('form').serializeJSON();// arr is an object =>
{'arr': {'0': 'foo', '1': 'var', '5': 'inn' }}
Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.
Use the option useIntKeysAsArrayIndex
to interpret integers as array indexes:
$('form').serializeJSON({useIntKeysAsArrayIndex: true});// arr is an array =>
{'arr': ['foo', 'var', undefined, undefined, undefined, 'inn']}
Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.
Defaults
All options defaults are defined in $.serializeJSON.defaultOptions
. You can just modify it to avoid setting the option on every call to serializeJSON
.
For example:
$.serializeJSON.defaultOptions.parseAll = true; // parse booleans, numbers and nulls by default$('form').serializeJSON(); // No options => then use $.serializeJSON.defaultOptions// returns =>
{
"bool": {
"true": true,
"false": false,
}
"number": {
"0": 0,
"1": 1,
"2.2": 2.2,
"-2.25": -2.25,
}
"null": null,
"string": "text is always string",
"empty": ""
}
Alternatives
I found others solving the same problem:
- https://github.com/macek/jquery-serialize-object
- https://github.com/hongymagic/jQuery.serializeObject
- https://github.com/danheberden/jquery-serializeForm
- https://github.com/maxatwork/form2js (plain js, no jQuery)
- https://github.com/serbanghita/formToObject.js (plain js, no jQuery)
- https://gist.github.com/shiawuen/2634143 (simpler but small)
But none of them checked what I needed at the time serializeJSON
was created. Factors that differentiate serializeJSON
from most of the alternatives: