237

How do I convert the entries from a HTML5 FormData object to JSON?

The solution should not use jQuery. Also, it should not simply serialize the entire FormData object, but only its key/value entries.

janw
  • 8,758
  • 11
  • 40
  • 62
Leonardo Villela
  • 2,498
  • 2
  • 12
  • 9
  • 2
    What are you trying to do? Does `JSON.stringify()` helps? Maybe you try to fix something that may be done in other way? – Justinas Jan 03 '17 at 07:39
  • 8
    Is not duplicate since I do not want to convert a javascript object to a json, nor do I want to use Jquery.serialize () – Leonardo Villela Jan 03 '17 at 13:34
  • check this : https://stackoverflow.com/a/39248551/6293856 – Bhavik Hirani Jul 23 '19 at 07:10
  • Works for nested forms too: https://stackoverflow.com/a/70057955/2377343 – T.Todua Nov 21 '21 at 20:04
  • Much depends on the goal you are pursuing. If you want send JSON through the XHR request, you don't need the conversion that has issue with multiple values. You can just use URLSearchParams API like: `let data = new URLSearchParams(formData)` – dlnsk Nov 23 '22 at 12:28

32 Answers32

318

You could also use forEach on the FormData object directly:

var object = {};
formData.forEach(function(value, key){
    object[key] = value;
});
var json = JSON.stringify(object);

UPDATE:

And for those who prefer the same solution with ES6 arrow functions:

var object = {};
formData.forEach((value, key) => object[key] = value);
var json = JSON.stringify(object);

UPDATE 2:

And for those who want support for multi select lists or other form elements with multiple values (since there are so many comments below the answer regarding this issue I will add a possible solution):

var object = {};
formData.forEach((value, key) => {
    // Reflect.has in favor of: object.hasOwnProperty(key)
    if(!Reflect.has(object, key)){
        object[key] = value;
        return;
    }
    if(!Array.isArray(object[key])){
        object[key] = [object[key]];    
    }
    object[key].push(value);
});
var json = JSON.stringify(object);

Here a Fiddle demonstrating the use of this method with a simple multi select list.

UPDATE 3:

As a side note for those ending up here; in case the purpose of converting the form data to json is to send it through an XMLHttpRequest to a server you can send the FormData object directly without converting it. As simple as this:

var request = new XMLHttpRequest();
request.open('POST', 'http://example.com/submitform.php');
request.send(formData);

See also Using FormData Objects on MDN for reference.

Or alternatively you can do the same with the modern day Fetch API:

fetch('http://example.com/submitform.php', {
  method: 'POST',
  body: formData
}).then((response) => { 
  // do something with response here... 
});

See also Using The Fetch API on MDN for reference.

UPDATE 4:

As mentioned in one of the comments below my answer the JSON stringify method won't work out of the box for all types of objects. For more information on what types are supported I would like to refer to the Description section in the MDN documentation of JSON.stringify.

In the description is also mentioned that:

If the value has a toJSON() method, it's responsible to define what data will be serialized.

This means that you can supply your own toJSON serialization method with logic for serializing your custom objects. Like that you can quickly and easily build serialization support for more complex object trees.

Wilt
  • 41,477
  • 12
  • 152
  • 203
  • 1
    As mentioned in the answer from @TomasPrado make sure you don't need support for IE11. – Wilt Feb 23 '18 at 08:44
  • 6
    This doesn't work with multi select form elements since they share the same key you end up overwriting the values returning only the last selected element – Sean Apr 02 '18 at 05:18
  • 1
    @Sean I gave an answer that works with multiple values for `` with same name, by converting the value to an array. – some Apr 14 '18 at 00:32
  • formData is not a serie. how you can iterate? accepted answer but something missed. – Nuri YILMAZ Jun 28 '18 at 15:24
  • @mayank In which browser does it not work? If you are using internet explorer 11 or older then it was already mentioned in a comment earlier... – Wilt Nov 27 '18 at 20:07
  • Chrome V-70, foreach need to be added as prototype function to formData. than only I can use. – Mayank Nov 27 '18 at 23:33
  • If you are using `fetch` with this, make sure you use the correct headers with `application/json` – Pro Q Feb 24 '19 at 05:05
  • when input has same name, this method going bad ,that only last element input shows. why this > – Dhanushka sasanka Jul 23 '19 at 20:53
  • @Dhanushkasasanka It happens because elements with the same key end up overwriting each other. See also the other comments. I added another update that should resolve this problem. – Wilt Jul 27 '19 at 07:22
  • Accepted answer and does not convert the value to the appropriate type? What if the value is a BLOB or a File type? – Mohy Eldeen Sep 30 '19 at 22:15
  • @MohyEldeen JSON `stringify` will not work for all types, I would like to refer [to this answer](https://stackoverflow.com/a/29281243/1697459) on another question that might help you solve your issue. One suggestion is to add a `toJSON` method to your object, I hope it will be of help. – Wilt Oct 01 '19 at 07:31
  • 14
    Unless a need for multi selects etc.. the answer with `JSON.stringify(Object.fromEntries(formData));` is so much nicer – Tom Stickel Nov 06 '19 at 21:15
  • It's ridiculous that it doesn't have a helper that does this. This should not be this difficult. – user2490003 Apr 15 '20 at 18:44
  • @Wilt, please add that in your UPDATE 2 code will be some problem with input name. If i make name for input with square brackets, this brackets will send to server. But if we use standard form submit, php will receive name without brackets. – Emil Sabitov May 17 '20 at 14:46
  • does not convert field if you have input type file on the form – sairfan Jul 24 '20 at 20:02
  • `formData.forEach` causes `Uncaught Error: Failed to execute 'invoke' on 'ForEachIteratorCallback': The provided callback is no longer runnable.` for me. – Mike T Sep 28 '22 at 15:48
  • 5.5 years after the original question, this answer's Update 2, however verbose, still seems the most complete (several other clever and shorter answers below, but none that deals with multiple values, e. g. from checkboxes). I'm surprised that there isn't a native method for this. – Nicolas Le Thierry d'Ennequin Oct 30 '22 at 10:16
307

In 2019, this kind of task became super-easy.

JSON.stringify(Object.fromEntries(formData));

Object.fromEntries: Supported in Chrome 73+, Firefox 63+, Safari 12.1

As mentioned in the comments, please note: FormData can contain multiple values with the same key (e.g. checkboxes with the same name). Object.fromEntries() throws away duplicates and only keeps the last one.

smhg
  • 2,159
  • 19
  • 26
hakatashi
  • 9,006
  • 2
  • 22
  • 22
  • 35
    This doesn't seem to work correctly on forms that have multiple fields with same name. – JukkaP Sep 08 '19 at 18:05
  • 9
    It is 2020 and this do not handle multiple selected values of `` – some Jan 02 '20 at 05:11
  • 7
    Better to use formData.entries: ```JSON.stringify(Object.fromEntries(formData.entries()));``` – Kohver Mar 12 '20 at 15:20
  • 12
    @Kohver, better why? Does it remove duplicates? What kind of problems does it solve to make it better? – CervEd Feb 23 '21 at 15:09
  • 2
    Are we sure this works correctly? I tried it for FormData that had multiple values on the same key, and it only printed the last one in the JSON string. – mhively Mar 18 '22 at 22:48
  • for multiple select or checkbox: https://stackoverflow.com/a/76188183/3414180 – Mingfei May 06 '23 at 09:42
32

Here's a way to do it in a more functional style, without the use of a library.

Array.from(formData.entries()).reduce((memo, [key, value]) => ({
  ...memo,
  [key]: value,
}), {});

Example:

document.getElementById('foobar').addEventListener('submit', (e) => {
  e.preventDefault();

  const formData = new FormData(e.target);
  const data = Array.from(formData.entries()).reduce((memo, [key, value]) => ({
    ...memo,
    [key]: value,
  }), {});
  document.getElementById('output').innerHTML = JSON.stringify(data);
});
<form id='foobar'>
  <input name='baz' />
  <input type='submit' />
</form>

<pre id='output'>Input some value and submit</pre>
dzuc
  • 761
  • 8
  • 12
15

If you have multiple entries with the same name, for example if you use <SELECT multiple> or have multiple <INPUT type="checkbox"> with the same name, you need to take care of that and make an array of the value. Otherwise you only get the last selected value.

Here is the modern ES6-variant:

function formToJSON( elem ) {
  let output = {};
  new FormData( elem ).forEach(
    ( value, key ) => {
      // Check if property already exist
      if ( Object.prototype.hasOwnProperty.call( output, key ) ) {
        let current = output[ key ];
        if ( !Array.isArray( current ) ) {
          // If it's not an array, convert it to an array.
          current = output[ key ] = [ current ];
        }
        current.push( value ); // Add the new value to the array.
      } else {
        output[ key ] = value;
      }
    }
  );
  return JSON.stringify( output );
}

Slightly older code (but still not supported by IE11, since it doesn't support ForEach or entries on FormData)

function formToJSON( elem ) {
  var current, entries, item, key, output, value;
  output = {};
  entries = new FormData( elem ).entries();
  // Iterate over values, and assign to item.
  while ( item = entries.next().value )
    {
      // assign to variables to make the code more readable.
      key = item[0];
      value = item[1];
      // Check if key already exist
      if (Object.prototype.hasOwnProperty.call( output, key)) {
        current = output[ key ];
        if ( !Array.isArray( current ) ) {
          // If it's not an array, convert it to an array.
          current = output[ key ] = [ current ];
        }
        current.push( value ); // Add the new value to the array.
      } else {
        output[ key ] = value;
      }
    }
    return JSON.stringify( output );
  }
some
  • 48,070
  • 14
  • 77
  • 93
  • 1
    But when only 1 checked in checkbox, the value is still a single string, intead of being an array. – ArtixZ Nov 07 '21 at 19:34
  • That is correct. Since you can have multiple type of fields with the same name, I made the decision to set the value for first occurrence, and only convert it to an array if multiple values was found. I do not want to first scan the form to find field names that could result in arrays. – some Nov 09 '21 at 15:23
15

I've seen no mentions of FormData.getAll method so far.

Besides returning all the values associated with a given key from within a FormData object, it gets really simple using the Object.fromEntries method as specified by others here.

var formData = new FormData(document.forms[0])

var obj = Object.fromEntries(
  Array.from(formData.keys()).map(key => [
    key, formData.getAll(key).length > 1 ? 
      formData.getAll(key) : formData.get(key)
  ])
)

Snippet in action

var formData = new FormData(document.forms[0])

var obj = Object.fromEntries(Array.from(formData.keys()).map(key => [key, formData.getAll(key).length > 1 ? formData.getAll(key) : formData.get(key)]))

document.write(`<pre>${JSON.stringify(obj)}</pre>`)
<form action="#">
  <input name="name" value="Robinson" />
  <input name="items" value="Vin" />
  <input name="items" value="Fromage" />
  <select name="animals" multiple id="animals">
    <option value="tiger" selected>Tigre</option>
    <option value="turtle" selected>Tortue</option>
    <option value="monkey">Singe</option>
  </select>
</form>
OpSocket
  • 997
  • 11
  • 19
12

If you need support for serializing nested fields, similar to how PHP handles form fields, you can use the following function

function update(data, keys, value) {
  if (keys.length === 0) {
    // Leaf node
    return value;
  }

  let key = keys.shift();
  if (!key) {
    data = data || [];
    if (Array.isArray(data)) {
      key = data.length;
    }
  }

  // Try converting key to a numeric value
  let index = +key;
  if (!isNaN(index)) {
    // We have a numeric index, make data a numeric array
    // This will not work if this is a associative array 
    // with numeric keys
    data = data || [];
    key = index;
  }
  
  // If none of the above matched, we have an associative array
  data = data || {};

  let val = update(data[key], keys, value);
  data[key] = val;

  return data;
}

function serializeForm(form) {
  return Array.from((new FormData(form)).entries())
    .reduce((data, [field, value]) => {
      let [_, prefix, keys] = field.match(/^([^\[]+)((?:\[[^\]]*\])*)/);

      if (keys) {
        keys = Array.from(keys.matchAll(/\[([^\]]*)\]/g), m => m[1]);
        value = update(data[prefix], keys, value);
      }
      data[prefix] = value;
      return data;
    }, {});
}

document.getElementById('output').textContent = JSON.stringify(serializeForm(document.getElementById('form')), null, 2);
<form id="form">
  <input name="field1" value="Field 1">
  <input name="field2[]" value="Field 21">
  <input name="field2[]" value="Field 22">
  <input name="field3[a]" value="Field 3a">
  <input name="field3[b]" value="Field 3b">
  <input name="field3[c]" value="Field 3c">
  <input name="field4[x][a]" value="Field xa">
  <input name="field4[x][b]" value="Field xb">
  <input name="field4[x][c]" value="Field xc">
  <input name="field4[y][a]" value="Field ya">
  <input name="field5[z][0]" value="Field z0">
  <input name="field5[z][]" value="Field z1">
  <input name="field6.z" value="Field 6Z0">
  <input name="field6.z" value="Field 6Z1">
</form>

<h2>Output</h2>
<pre id="output">
</pre>
Joyce Babu
  • 19,602
  • 13
  • 62
  • 97
9

You can achieve this by using the FormData() object. This FormData object will be populated with the form's current keys/values using the name property of each element for the keys and their submitted value for the values. It will also encode file input content.

Example:

var myForm = document.getElementById('myForm');
myForm.addEventListener('submit', function(event)
{
    event.preventDefault();
    var formData = new FormData(myForm),
        result = {};

    for (var entry of formData.entries())
    {
        result[entry[0]] = entry[1];
    }
    result = JSON.stringify(result)
    console.log(result);

});
GiriB
  • 166
  • 5
  • This does not produce json – Liam Jan 03 '17 at 08:54
  • 1
    @Liam Have you tried this with form elements ? And let me know why it doesn't produce JSON object ? – GiriB Jan 03 '17 at 09:37
  • 1
    There is no such thing as a json object. Json is a string notation – Liam Jan 03 '17 at 10:13
  • 1
    @Liam After the object created they can use JSON.stringify(result). And I have edited my answer. Please check it. And withdraw the down vote. – GiriB Jan 03 '17 at 14:10
  • 1
    You can also make the for of statement more expressive if you're using ES6: `for (const [key, value] of formData.entries())` – Teddy Zetterlund Mar 22 '17 at 08:15
  • Warning to newcomers that copy/paste this: bear in mind that `for of` expression does not work in IE11 and other browsers that are not compatible with ES6. http://kangax.github.io/compat-table/es6/ – Tomás Sep 08 '17 at 11:19
  • you will run into issues if the query use an array form for the query key, for example something like this: [a][b][], you will not get all the checked values as you should – mr1031011 Jan 03 '18 at 11:43
7

This post is already a year old... but, I really, really like the ES6 @dzuc answer. However it is incomplete by not been able to handle multiple selects or checkboxes. This has already pointed and code solutions has been offered. I find them heavy and not optimized. So I wrote a 2 versions based on @dzuc to handle these cases:

  • For ASP style forms where multiple items name could just simple repeated.
let r=Array.from(fd).reduce(
  (o , [k,v]) => (
     (!o[k])
     ? {...o , [k] : v}
     : {...o , [k] : [...o[k] , v]}
   )
   ,{}
);
let obj=JSON.stringify(r);

One line Hotshot version:

Array.from(fd).reduce((o,[k,v])=>((!o[k])?{...o,[k]:v}:{...o,[k]:[...o[k],v]}),{});
  • For PHP style forms where the multiple item names must have a [] suffix.
let r=Array.from(fd).reduce(
  (o , [k,v]) => (
    (k.split('[').length>1)
    ? (k=k.split('[')[0]
      , (!o[k])
      ? {...o , [k] : [v]}
      : {...o , [k] : [...o[k] , v ]}
    )
    : {...o , [k] : v}
  )
  ,{}
);
let obj=JSON.stringify(r);

One line Hotshot version:

Array.from(fd).reduce((o,[k,v])=>((k.split('[').length>1)?(k=k.split('[')[0],(!o[k])?{...o,[k]:[v]}:{...o,[k]:[...o[k],v]}):{...o,[k]:v}),{});
  • Extension of PHP form that support multi-level arrays.

Since last time I wrote the previous second case, at work it came a case that the PHP form has checkboxes on multi-levels. I wrote a new case to support previous case and this one. I created a snippet to better showcase this case, the result show on the console for this demo, modify this to your need. Tried to optimize it the best I could without compromising performance, however, it compromise some human readability. It takes advantage that arrays are objects and variables pointing to arrays are kept as reference. No hotshot for this one, be my guest.

let nosubmit = (e) => {
  e.preventDefault();
  const f = Array.from(new FormData(e.target));
  const obj = f.reduce((o, [k, v]) => {
    let a = v,
      b, i,
      m = k.split('['),
      n = m[0],
      l = m.length;
    if (l > 1) {
      a = b = o[n] || [];
      for (i = 1; i < l; i++) {
        m[i] = (m[i].split(']')[0] || b.length) * 1;
        b = b[m[i]] = ((i + 1) == l) ? v : b[m[i]] || [];
      }
    }
    return { ...o, [n]: a };
  }, {});
  console.log(obj);
}
document.querySelector('#theform').addEventListener('submit', nosubmit, {capture: true});
<h1>Multilevel Form</h1>
<form action="#" method="POST" enctype="multipart/form-data" id="theform">
  <input type="hidden" name="_id" value="93242" />
  <input type="hidden" name="_fid" value="45c0ec96929bc0d39a904ab5c7af70ef" />
  <label>Select:
    <select name="uselect">
      <option value="A">A</option>
      <option value="B">B</option>
      <option value="C">C</option>
    </select>
  </label>
  <br /><br />
  <label>Checkboxes one level:<br/>
    <input name="c1[]" type="checkbox" checked value="1"/>v1 
    <input name="c1[]" type="checkbox" checked value="2"/>v2
    <input name="c1[]" type="checkbox" checked value="3"/>v3
  </label>
  <br /><br />
  <label>Checkboxes two levels:<br/>
    <input name="c2[0][]" type="checkbox" checked value="4"/>0 v4 
    <input name="c2[0][]" type="checkbox" checked value="5"/>0 v5
    <input name="c2[0][]" type="checkbox" checked value="6"/>0 v6
    <br/>
    <input name="c2[1][]" type="checkbox" checked value="7"/>1 v7 
    <input name="c2[1][]" type="checkbox" checked value="8"/>1 v8
    <input name="c2[1][]" type="checkbox" checked value="9"/>1 v9
  </label>
  <br /><br />
  <label>Radios:
    <input type="radio" name="uradio" value="yes">YES
    <input type="radio" name="uradio" checked value="no">NO
  </label>
  <br /><br />
  <input type="submit" value="Submit" />
</form>
CarlosH.
  • 711
  • 7
  • 12
  • 2
    `Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});` hotshot version es2018 – nackjicholson Feb 05 '19 at 00:45
  • 1
    @nackjicholson Yeap, you are right, omitting .entries() and the [k,v] element simplify the code. I will rewrite the code to include these improvements. However yours will still overwrite on repeated values. – CarlosH. Feb 06 '19 at 05:09
  • 15
    While I an appreciate the effort - code like this is absurd. Nobody wants to look at letters for variables and objects, this is not 1985. – Tom Stickel Nov 06 '19 at 21:12
6

Easy To Use Function

I Have Created A Function For This

function FormDataToJSON(FormElement){    
    var formData = new FormData(FormElement);
    var ConvertedJSON= {};
    for (const [key, value]  of formData.entries())
    {
        ConvertedJSON[key] = value;
    }

    return ConvertedJSON
}

Example Usage

var ReceivedJSON = FormDataToJSON(document.getElementById('FormId'));

In this code I have created empty JSON variable using for loop I have used keys from formData Object to JSON Keys in every Itration.

You Find This Code In My JS Library On GitHub Do Suggest Me If It Needs Improvement I Have Placed Code Here https://github.com/alijamal14/Utilities/blob/master/Utilities.js

Ali Jamal
  • 1,383
  • 1
  • 13
  • 20
5

Here is a function that turns a formData object into a JSON-string.

  • Works with multiple entries and nested arrays.
  • Works with both numbered and named input name arrays.

For example you can have the following form fields:

<select name="select[]" multiple></select>
<input name="check[a][0][]" type="checkbox" value="test"/>

Usage:

let json = form2json(formData);

The function:

function form2json(data) {
        
        let method = function (object,pair) {
            
            let keys = pair[0].replace(/\]/g,'').split('[');
            let key = keys[0];
            let value = pair[1];
            
            if (keys.length > 1) {
                
                let i,x,segment;
                let last = value;
                let type = isNaN(keys[1]) ? {} : [];
                
                value = segment = object[key] || type;
                
                for (i = 1; i < keys.length; i++) {
                    
                    x = keys[i];
                    
                    if (i == keys.length-1) {
                        if (Array.isArray(segment)) {
                            segment.push(last);
                        } else {
                            segment[x] = last;
                        }
                    } else if (segment[x] == undefined) {
                        segment[x] = isNaN(keys[i+1]) ? {} : [];
                    }
                    
                    segment = segment[x];
                    
                }
                
            }
            
            object[key] = value;
            
            return object;
            
        }
        
        let object = Array.from(data).reduce(method,{});
        
        return JSON.stringify(object);
        
    }
frankichiro
  • 71
  • 2
  • 4
  • 1
    This, for me, seems to be the best answer and covers the variety of possible input names. Thanks for sharing. (Just add `function` before the first statement and you are golden.) – z-x Mar 22 '22 at 12:27
  • `function` added, thanks! – frankichiro Mar 23 '22 at 14:16
4

If the following items meet your needs, you're in luck:

  1. You want to convert an array of arrays like [['key','value1'], ['key2','value2'] (like what FormData gives you) into a key->value object like {key1: 'value1', key2: 'value2'} and the convert it to a JSON string.
  2. You are targeting browsers/devices with the latest ES6 interpreter or are compiling with something like babel.
  3. You want the tiniest way to accomplish this.

Here is the code you'll need:

const data = new FormData(document.querySelector('form'));
const json = JSON.stringify(Array.from(data).reduce((o,[k,v])=>(o[k]=v,o),{}));

Hope this helps someone.

KyleFarris
  • 17,274
  • 5
  • 40
  • 40
4

I think this is the simplest way to get the result you want from a formData FormData object:

const jsonData = {};

for(const [key, value] of formData) {
    jsonData[key] = value;
}
Red Krow
  • 49
  • 1
3

Even though the answer from @dzuc is already very good, you could use array destructuring (available in modern browsers or with Babel) to make it even a bit more elegant:

// original version from @dzuc
const data = Array.from(formData.entries())
  .reduce((memo, pair) => ({
    ...memo,
    [pair[0]: pair[1],
  }), {})

// with array destructuring
const data = Array.from(formData.entries())
  .reduce((memo,[key, value]) => ({
    ...memo,
    [key]: value,
  }), {})
Jeremias Erbs
  • 1,925
  • 2
  • 13
  • 7
3

Abusive one-liner!

Array.from(fd).reduce((obj, [k, v]) => ({...obj, [k]: v}), {});

Today I learned firefox has object spread support and array destructuring!

nackjicholson
  • 4,557
  • 4
  • 37
  • 35
2

If you are using lodash it can be done concisely with fromPairs

import {fromPairs} from 'lodash';

const object = fromPairs(Array.from(formData.entries()));
Erik van Velzen
  • 6,211
  • 3
  • 23
  • 23
2

You can try this

formDataToJSON($('#form_example'));

# Create a function to convert the serialize and convert the form data
# to JSON
# @param : $('#form_example');
# @return a JSON Stringify
function formDataToJSON(form) {
    let obj = {};
    let formData = form.serialize();
    let formArray = formData.split("&");

    for (inputData of formArray){
        let dataTmp = inputData.split('=');
        obj[dataTmp[0]] = dataTmp[1];
    }
    return JSON.stringify(obj);
}
Ivan Fretes
  • 668
  • 7
  • 11
2

Another approach that works with select multiple or inputs with same name attribute:

function form_to_json() {
  const form_data = new FormData(document.querySelector('form'))
  const uniqueKeys = [...new Set(form_data.keys())]  
  const obj = {}
  uniqueKeys.forEach((value, key) => {
    obj[value] = (form_data.getAll(value).length > 1) ? form_data.getAll(value) : form_data.get(value)
  })
  const json = JSON.stringify(obj)
  alert(json)
}
<form>
  <input type="text" name="name" value="Cesar"></br>
  <select name="cars" id="cars" multiple>
    <option value="volvo" selected>Volvo</option>
    <option value="saab" selected>Saab</option>
  </select>
  <input type="button" onclick="form_to_json()" value="Ok">
</form>
Cesar Morillas
  • 707
  • 5
  • 11
2

EDIT: I saw there already is an answer which yields very similar results.

Two main differences:

  • This doesn't treat numeric keys as array indices, rather they still count as object keys. Might not be the proper behaviour but I've personally never written forms that use this notation, might update it.
  • This is not a recursive function, this is obviously not a pro or a con

Ignore any lack of efficiency. This will handle unlimited nesting, and duplicate keys for arrays. This obviously will not convert things such as files, but I don't need it so I didn't add it.

Here's a JSFiddle with an example conversion that can be achieved with this function

A great example usage for this (which is also my use case) would be to create a new FormData object from an html form element, and easily convert it to JSON for sending.

/**
 * @param {FormData} formData
 * @return {Object}
 */
function formDataToObject(formData) {
    const object = {};
    for (let pair of formData.entries()) {
        const key = pair[0];
        const value = pair[1];
        const isArray = key.endsWith('[]');
        const name = key.substring(0, key.length - (2 * isArray));
        const path = name.replaceAll(']', '');
        const pathParts = path.split('[');
        const partialsCount = pathParts.length;
        let iterationObject = object;
        for (let i = 0; i < partialsCount; i++) {
            let part = pathParts[i];
            let iterationObjectElement = iterationObject[part];
            if (i !== partialsCount - 1) {
                if (!iterationObject.hasOwnProperty(part) || typeof iterationObjectElement !== "object") {
                    iterationObject[part] = {};
                }
                iterationObject = iterationObject[part];
            } else {
                if (isArray) {
                    if (!iterationObject.hasOwnProperty(part)) {
                        iterationObject[part] = [value];
                    } else {
                        iterationObjectElement.push(value);
                    }
                } else {
                    iterationObject[part] = value;
                }
            }
        }
    }

    return object;
}
Guido Belluomo
  • 168
  • 1
  • 6
1

Worked for me

                var myForm = document.getElementById("form");
                var formData = new FormData(myForm),
                obj = {};
                for (var entry of formData.entries()){
                    obj[entry[0]] = entry[1];
                }
                console.log(obj);
Shahid Hussain Abbasi
  • 2,508
  • 16
  • 10
1

Making use of toJSON as described in JSON.stringify()

If the value has a toJSON() method, it's responsible to define what data will be serialized.

Here is a little hack.

var fd = new FormData(document.forms[0]);

fd.toJSON = function() {
  const o = {};
  this.forEach((v, k) => {
    v = this.getAll(k);
    o[k] = v.length == 1 ? v[0] : (v.length >= 1 ? v : null);
  });
  return o;
};

document.write(`<pre>${JSON.stringify(fd)}</pre>`)
<form action="#">
  <input name="name" value="Robinson" />
  <input name="items" value="Vin" />
  <input name="items" value="Fromage" />
  <select name="animals" multiple id="animals">
    <option value="tiger" selected>Tigre</option>
    <option value="turtle" selected>Tortue</option>
    <option value="monkey">Singe</option>
  </select>
</form>
Umair Khan
  • 1,684
  • 18
  • 34
1

This solved my issue and this is for an Object

const formDataObject = (formData) => {

    for (const key in formData) {
        if (formData[key].startsWith('{') || formData[key].startsWith('[')) {
            try {
                formData[key] = JSON.parse(formData[key]);
                console.log("key is :", key, "form data is :", formData[key]);

            } catch (error) {
                console.log("error :", key);
            }
        }
    }

    console.log("object", formData)
}
1

just use:

JSON.stringify(Object.fromEntries(formData));

for checkbox input or multiple select, use getAll function:

let formData = new FormData(form);
let obj = Object.fromEntries(new FormData(form));
let hobbies = formData.getAll('hobbies'); // hobbies is checkbox's name
let data = {...obj, hobbies: JSON.stringify(hobbies)};
Mingfei
  • 1,459
  • 1
  • 10
  • 12
0

In my case form Data was data , fire base was expecting an object but data contains object as well as all other stuffs so i tried data.value it worked!!!

Rahul solanki
  • 83
  • 1
  • 4
0

I am arriving late here. However, I made a simple method that checks for the input type="checkbox"

var formData = new FormData($form.get(0));
        var objectData = {};
        formData.forEach(function (value, key) {
            var updatedValue = value;
            if ($('input[name="' + key + '"]').attr("type") === "checkbox" && $('input[name="' + key + '"]').is(":checked")) {
                updatedValue = true; // we don't set false due to it is by default on HTML
            }
            objectData[key] = updatedValue;
        });
var jsonData = JSON.stringify(objectData);

I hope this helps somebody else.

vhugo
  • 543
  • 1
  • 5
  • 23
0

if you want the json string :

var jsonStr =  JSON.stringify($('#formId').serializeObject());

if you want a json Object

var jsonObj =  JSON.parse(JSON.stringify($('#formId').serializeObject()));
Bilal
  • 1,254
  • 13
  • 14
0

Years later in vanillaJs (es6)

let body = new FormData()

body.set('key1', 'value AA')

body.set('key2', 'value BB')

let data = [...body.keys()].reduce( (acc, key, idx) => {
    acc[key] = body.get(key)
    return acc
} , {} )

console.log(JSON.stringify(data)) // {key1: 'value AA', key2: 'value BB'}

Wolfgang Leon
  • 695
  • 9
  • 20
0

Here's a way that includes a simple way to handle multiple values. If a key ends with [], it combines the values into an array (like in the old php convention), and drops the [] from the key:

function formDataToJson(f) {
  return Object.fromEntries(Array.from(f.keys(), k =>
    k.endsWith('[]') ? [k.slice(0, -2), f.getAll(k)] : [k, f.get(k)]));
}

For example, if you have:

const f = new FormData();
f.append("a", "1");
f.append("a", "2");
f.append("b[]", "3");
f.append("b[]", "4");
formDataToJson(f)  // --> produces {a: "1", b: ["3","4"]}
DS.
  • 22,632
  • 6
  • 47
  • 54
0

For my purposes that included multiselects like checkboxes, this was good:

JSON.stringify(Array.from((new FormData(document.querySelector('form'))).entries()).reduce((map = {}, [key, value]) => {
    return {
        ...map,
        [key]: map[key] ? [...map[key], value] : value,
    };
}, {}));
morewry
  • 4,320
  • 3
  • 35
  • 35
0

Here is a function to convert FormData to plain JavaScript object which can be converted to JSON.

function formDataToObject(formData) {
    let object = {}

    const debug = (message) => {
        //console.log(message)
    }

    /**
     * Parses FormData key xxx`[x][x][x]` fields into array
     */
    const parseKey = (key) => {
        const subKeyIdx = key.indexOf('[');

        if (subKeyIdx !== -1) {
            const keys = [key.substring(0, subKeyIdx)]
            key = key.substring(subKeyIdx)

            for (const match of key.matchAll(/\[(?<key>.*?)]/gm)) {
                keys.push(match.groups.key)
            }
            return keys
        } else {
            return [key]
        }
    }

    /**
     * Recursively iterates over keys and assigns key/values to object
     */
    const assign = (keys, value, object) => {
        const key = keys.shift()
        debug(key)
        debug(keys)

        // When last key in the iterations
        if (key === '' || key === undefined) {
            return object.push(value)
        }

        if (Reflect.has(object, key)) {
            debug('hasKey ' + key)
            // If key has been found, but final pass - convert the value to array
            if (keys.length === 0) {
                if (!Array.isArray(object[key])) {
                    debug('isArray ' + object[key])
                    object[key] = [object[key], value]
                    return
                }
            }
            // Recurse again with found object
            return assign(keys, value, object[key])
        }

        // Create empty object for key, if next key is '' do array instead, otherwise set value
        if (keys.length >= 1) {
            debug(`undefined '${key}' key: remaining ${keys.length}`)
            object[key] = keys[0] === '' ? [] : {}
            return assign(keys, value, object[key])
        } else {
            debug("set value: " + value)
            object[key] = value
        }
    }

    for (const pair of formData.entries()) {
        assign(parseKey(pair[0]), pair[1], object)
    }

    return object
}

var formData = new FormData(document.querySelector('form'))

object = formDataToObject(formData)

console.log(object)
<form id="form">
<input type="text" name="test" value="BBBB">
<input type="text" name="testX" value="WEE">
<input type="text" name="testX" value="FFF">
<input type="text" name="testX[]" value="1">
<input type="text" name="test1[]" value="2">
<input type="text" name="test1[]" value="3">
<input type="text" name="test1[]" value="4">
<input type="text" name="test1[]" value="5">
<input type="text" name="test2[a]" value="5">
<input type="text" name="test2[a]" value="77">
<input type="text" name="test3[a]" value="67">
<input type="text" name="test3[1][]" value="22">
<input type="text" name="test3[1][]" value="33">
<input type="text" name="test3[1][]" value="44">
<input type="text" name="test4[xAx][1]" value="3">
<input type="text" name="test4[xAx][2]" value="23">
<input type="text" name="test4[xAx][3]" value="33">
</form>
Ako
  • 956
  • 1
  • 10
  • 13
0

+1 with everyone who says it's annoying that there isn't an inbuilt way to do this!

One thing worth noting is that if only one option is selected in a multi select, many answers including the accepted answer will only return a string with a single value instead of an array (for example, try clicking only "cats" on the accepted answer and you will get {"pets":"cats"} instead of {"pets":["cats"]}). This may not be an issue depending on use case, but if you are using an API which is expect an array instead of a single string this will cause an exception. To account for this, we can get all select elements with "multiple" as an attribute. Here is code based on the accepted answer which accounts for this case:

    const submit = document.getElementById("submit");

const getFormInfo = () => {
  const form = document.getElementById("form");
  let allMultiSelectFields = []
  document.querySelectorAll('select[multiple]').forEach(element => allMultiSelectFields.push(element.getAttribute("name")))
  return {
    formData: new FormData(form),
    mutliSelectFields: allMultiSelectFields
  }
}

const toJson = function(event) {
  const {
    formData,
    mutliSelectFields
  } = getFormInfo()
  event.preventDefault();
  let object = {};
  formData.forEach((value, key) => {
    if (mutliSelectFields.includes(key)) {
      if (Reflect.has(object, key)) object[key].push(value)
      else object[key] = [value]
      return
    }
    object[key] = value
  });
  let json = JSON.stringify(object);
  console.log(json);
};

submit.addEventListener("click", toJson);
-1

This works for me. Click here to see the details code

let formToJson = function(){
    let form = new FormData($("form#formData")[0]);
    //console.log(form);
    let jsonData = Object.fromEntries(form.entries());
    //console.log(jsonData);
    $("#jsonData").html(JSON.stringify(jsonData));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
    <div class="">
        <form action="#" method="post" id="formData">
            <input type="text" name="fName" id="fName" value="Mad"><br>
            <input type="text" name="lName" id="lName" value="Coder"><br>
            <input type="button" onclick="formToJson()" value="Boom Yeah">
        </form>
    </div>
    <div id="jsonData">
    </div>
</body>
</html>
-1

I know its late, but here's the solution;

new URLSearchParams(new FormData(formElement)).toString()

Now, this can be sent as the body. Sadly, its not JSON.

Akshay K Nair
  • 1,107
  • 16
  • 29