Перетворити об’єкт JS для формування даних


128

Як я можу конвертувати свій JS-об’єкт FormData?

Причиною, чому я хочу це зробити, є те, що у мене є об'єкт, який я сконструював із значень поля ~ 100 форми.

var item = {
   description: 'Some Item',
   price : '0.00',
   srate : '0.00',
   color : 'red',
   ...
   ...
}

Тепер мене просять додати функцію файлу для завантаження до моєї форми, що, звичайно, неможливо через JSON, і тому я планую перейти до FormData. То чи є який-небудь спосіб, до якого я можу перетворити свій об’єкт JS FormData?


чи можете ви поділитися своєю роботою / прогресом?
Ritikesh

як щодо JSON.stringify ()?
Сонячний Шарма

1
@Sunny - Це створить текст JSON в рядку. Це не FormDataоб’єкт.
Квентін

Так, ви можете, ви можете додати до об’єктівData.
adeneo

чи можете ви показати нам, що ви маєте на увазі під FormData? якийсь конкретний формат?
Сонячний Шарма

Відповіді:


153

Якщо у вас є об'єкт, ви можете легко створити об'єкт FormData та додати імена та значення цього об’єкта до formData.

Ви не опублікували жодного коду, тому це загальний приклад;

var form_data = new FormData();

for ( var key in item ) {
    form_data.append(key, item[key]);
}

$.ajax({
    url         : 'http://example.com/upload.php',
    data        : form_data,
    processData : false,
    contentType : false,
    type: 'POST'
}).done(function(data){
    // do stuff
});

У документації на MDN є більше прикладів


3
@Lior - itemце звичайний об'єкт, створений ОП, тому він не повинен мати жодних властивостей, які не є його власними, якщо хтось не помилився з прототипуванням чого-небудь на конструкторі Object, і в цьому випадку ви опинилися б у світі неприємностей , і це не те, що нам слід було б захистити.
adeneo

2
@Lior - це просто додавання клавіш / значення пар до FormData, додавання прототипованої властивості нічого не зламає, а використання Object.keysне є відповіддю, оскільки вам не доведеться отримувати ключі як масив, а потім перебирайте ключі, щоб отримати значення, ви повинні використовувати for..inцикл.
adeneo

2
Звичайно, буде, ви не знаєте, на що очікує сервер ... бо ... в JS є пробламним, рішення не повинно бути Object.keys (), це може бути hasOwnProperty (), але це повинно бути принаймні попередженням.
Lior

3
@Lior - Якщо ваш сервер ламається, коли отримує ще одну пару ключів / значень у запиті POST, ви робите це неправильно. Я вважаю, що відповідь чудовий, і я не збираюся змінювати його на використання Object.keysабо hasOwnProperty()як об’єкт розміщений у запитанні, і жоден із них не потребує. Причина, яку ви іноді бачите hasOwnPropertyв плагінах тощо, полягає в тому, що ви ніколи не знаєте, що деякі люди можуть робити Objectконструктору, але здебільшого люди не повинні перевіряти наявність успадкованих властивостей на створених ними об'єктах, це знак того ти, мабуть, робиш щось не так.
adeneo

5
@Lior Ви маєте намір будувати літаки із соломи поруч, сподіваючись, що залучіть справжніші літаки, які скидають їжу з неба? Важливо зрозуміти, чому використовується перевірка hasOwnProperty, просто кажучи, що речі вважаються "найкращою практикою", тому що ви читаєте чиюсь книгу (здогадуєтесь, Крокфорд) не дуже далеко, намагаючись навчити колегу. Члену є більше 100 разів репутація та 100-кратне число відповідей, які ви маєте, також не дуже допомагають вашій увазі. Крім того, назвіть одну нову сторонній вкладку, яка змінює прототип? Цей пост - з іншого часу ...
Бенджамін Грюнбаум,

84

З ES6 та більш функціональним підходом до програмування відповідь @ adeneo може виглядати так:

function getFormData(object) {
    const formData = new FormData();
    Object.keys(object).forEach(key => formData.append(key, object[key]));
    return formData;
}

І також, використовуючи .reduce()і стрілочні функції:

getFormData = object => Object.keys(object).reduce((formData, key) => {
    formData.append(key, object[key]);
    return formData;
}, new FormData());

44

Ця функція додає всі дані від об'єкта до FormData

Версія ES6 від @ developer033:

function buildFormData(formData, data, parentKey) {
  if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File)) {
    Object.keys(data).forEach(key => {
      buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key);
    });
  } else {
    const value = data == null ? '' : data;

    formData.append(parentKey, value);
  }
}

function jsonToFormData(data) {
  const formData = new FormData();

  buildFormData(formData, data);

  return formData;
}

const my_data = {
  num: 1,
  falseBool: false,
  trueBool: true,
  empty: '',
  und: undefined,
  nullable: null,
  date: new Date(),
  name: 'str',
  another_object: {
    name: 'my_name',
    value: 'whatever'
  },
  array: [
    {
      key1: {
        name: 'key1'
      }
    }
  ]
};

jsonToFormData(my_data)

Версія jQuery:

function appendFormdata(FormData, data, name){
    name = name || '';
    if (typeof data === 'object'){
        $.each(data, function(index, value){
            if (name == ''){
                appendFormdata(FormData, value, index);
            } else {
                appendFormdata(FormData, value, name + '['+index+']');
            }
        })
    } else {
        FormData.append(name, data);
    }
}


var formData = new FormData(),
    your_object = {
        name: 'test object',
        another_object: {
            name: 'and other objects',
            value: 'whatever'
        }
    };
appendFormdata(formData, your_object);

Nice Keep it up
Vivek Doshi

Працює дуже добре! Дякую! Я також повинен був додати && !(data instanceof Blob)у своєму випадку, щоб завантажити свої зображення
Clément Baconnier

Для мене добре працює, я додав if (typeof data === 'object' && data! == null) {тому що він кидав виняток, якщо значення null
al000y

Для версії ES6 я додав && !(Array.isArray(data) && !data.length)в умові "якщо" або порожній масив буде видалений.
Mtxz

14

Інші відповіді були для мене неповними. Я почав з відповіді @Vladimir Novopashin та модифікував її. Ось речі, які мені знадобилися, і помилка, яку я знайшов:

  • Підтримка файлу
  • Підтримка масиву
  • Помилка: файл всередині складного об'єкта потрібно додавати .propзамість, а не [prop]. Наприклад, formData.append('photos[0][file]', file)не працював на Google Chrome, поки formData.append('photos[0].file', file)працював
  • Ігноруйте деякі властивості в моєму об’єкті

Наступний код повинен працювати на IE11 та вічнозелених браузерах.

function objectToFormData(obj, rootName, ignoreList) {
    var formData = new FormData();

    function appendFormData(data, root) {
        if (!ignore(root)) {
            root = root || '';
            if (data instanceof File) {
                formData.append(root, data);
            } else if (Array.isArray(data)) {
                for (var i = 0; i < data.length; i++) {
                    appendFormData(data[i], root + '[' + i + ']');
                }
            } else if (typeof data === 'object' && data) {
                for (var key in data) {
                    if (data.hasOwnProperty(key)) {
                        if (root === '') {
                            appendFormData(data[key], key);
                        } else {
                            appendFormData(data[key], root + '.' + key);
                        }
                    }
                }
            } else {
                if (data !== null && typeof data !== 'undefined') {
                    formData.append(root, data);
                }
            }
        }
    }

    function ignore(root){
        return Array.isArray(ignoreList)
            && ignoreList.some(function(x) { return x === root; });
    }

    appendFormData(obj, rootName);

    return formData;
}

1
Єдина відповідь, що підтримує масиви, об'єкти та файли.
sotn

Привіт, чому ти додаєш файл у корінь? Чи можна його додавати і дитині?
Седрік Арнулд

@CedricArnould Це може бути непорозуміння, але метод є рекурсивним, тому навіть якщо він написаний formData.append(root, data), це не означає, що він додається до кореня.
Гудрадейн

Я розумію вашу відповідь, як не дивно, коли я отримую результат на сервері, у мене є унікальна колекція файлів і даних. Але я можу побачити у файлі ім'я, яке дає інформацію, до якої дитини він підключений. Можливо, проблема пов’язана з .Net Core та способом управління файлами завантаження. Дякую за вашу відповідь.
Седрік Арнульд

Я намагаюся використовувати це, але немає прикладу використання. очікуваний формат параметри ignoreList був би дуже корисним.
jpro

8

Спробуйте функцію JSON.stringify, як показано нижче

var postData = JSON.stringify(item);
var formData = new FormData();
formData.append("postData",postData );

1
Це найкращий спосіб досягти цього.
Роб

його додайте json після декількох разів налагодження помилок
Snow Bases

7

У мене був сценарій, коли вкладений JSON довелося серіалізувати лінійно, поки формуються дані форми, оскільки саме так сервер очікує значень. Отже, я написав невелику рекурсивну функцію, яка перекладає JSON приблизно так:

{
   "orderPrice":"11",
   "cardNumber":"************1234",
   "id":"8796191359018",
   "accountHolderName":"Raj Pawan",
   "expiryMonth":"02",
   "expiryYear":"2019",
   "issueNumber":null,
   "billingAddress":{
      "city":"Wonderland",
      "code":"8796682911767",
      "firstname":"Raj Pawan",
      "lastname":"Gumdal",
      "line1":"Addr Line 1",
      "line2":null,
      "state":"US-AS",
      "region":{
         "isocode":"US-AS"
      },
      "zip":"76767-6776"
   }
}

У щось подібне:

{
   "orderPrice":"11",
   "cardNumber":"************1234",
   "id":"8796191359018",
   "accountHolderName":"Raj Pawan",
   "expiryMonth":"02",
   "expiryYear":"2019",
   "issueNumber":null,
   "billingAddress.city":"Wonderland",
   "billingAddress.code":"8796682911767",
   "billingAddress.firstname":"Raj Pawan",
   "billingAddress.lastname":"Gumdal",
   "billingAddress.line1":"Addr Line 1",
   "billingAddress.line2":null,
   "billingAddress.state":"US-AS",
   "billingAddress.region.isocode":"US-AS",
   "billingAddress.zip":"76767-6776"
}

Сервер приймає дані форми, які перебувають у цьому перетвореному форматі.

Ось функція:

function jsonToFormData (inJSON, inTestJSON, inFormData, parentKey) {
    // http://stackoverflow.com/a/22783314/260665
    // Raj: Converts any nested JSON to formData.
    var form_data = inFormData || new FormData();
    var testJSON = inTestJSON || {};
    for ( var key in inJSON ) {
        // 1. If it is a recursion, then key has to be constructed like "parent.child" where parent JSON contains a child JSON
        // 2. Perform append data only if the value for key is not a JSON, recurse otherwise!
        var constructedKey = key;
        if (parentKey) {
            constructedKey = parentKey + "." + key;
        }

        var value = inJSON[key];
        if (value && value.constructor === {}.constructor) {
            // This is a JSON, we now need to recurse!
            jsonToFormData (value, testJSON, form_data, constructedKey);
        } else {
            form_data.append(constructedKey, inJSON[key]);
            testJSON[constructedKey] = inJSON[key];
        }
    }
    return form_data;
}

Виклик:

        var testJSON = {};
        var form_data = jsonToFormData (jsonForPost, testJSON);

Я використовую testJSON просто для перегляду конвертованих результатів, оскільки я не зможу витягнути вміст form_data. Поштовий дзвінок AJAX:

        $.ajax({
            type: "POST",
            url: somePostURL,
            data: form_data,
            processData : false,
            contentType : false,
            success: function (data) {
            },
            error: function (e) {
            }
        });

Привіт Радж, як щодо масивів? Скажіть, що у вас більше 1 платіжної адреси. Як би ви це виправили?
Сем

1
Я знайшов відповідь! Ваше повідомлення дуже корисне. Дякую!
Сем

3

Вибачте за пізню відповідь, але я боровся з цим, оскільки Angular 2 наразі не підтримує завантаження файлів. Отже, спосіб це зробити - це відправлення XMLHttpRequestс FormData. Отже, я створив функцію для цього. Я використовую Typescript . Щоб перетворити його в Javascript, просто видаліть декларацію про типи даних.

/**
     * Transforms the json data into form data.
     *
     * Example:
     *
     * Input:
     * 
     * fd = new FormData();
     * dob = {
     *  name: 'phone',
     *  photos: ['myphoto.jpg', 'myotherphoto.png'],
     *  price: '615.99',
     *  color: {
     *      front: 'red',
     *      back: 'blue'
     *  },
     *  buttons: ['power', 'volup', 'voldown'],
     *  cameras: [{
     *      name: 'front',
     *      res: '5Mpx'
     *  },{
     *      name: 'back',
     *      res: '10Mpx'
     *  }]
     * };
     * Say we want to replace 'myotherphoto.png'. We'll have this 'fob'.
     * fob = {
     *  photos: [null, <File object>]
     * };
     * Say we want to wrap the object (Rails way):
     * p = 'product';
     *
     * Output:
     *
     * 'fd' object updated. Now it will have these key-values "<key>, <value>":
     *
     * product[name], phone
     * product[photos][], myphoto.jpg
     * product[photos][], <File object>
     * product[color][front], red
     * product[color][back], blue
     * product[buttons][], power
     * product[buttons][], volup
     * product[buttons][], voldown
     * product[cameras][][name], front
     * product[cameras][][res], 5Mpx
     * product[cameras][][name], back
     * product[cameras][][res], 10Mpx
     * 
     * @param {FormData}  fd  FormData object where items will be appended to.
     * @param {Object}    dob Data object where items will be read from.
     * @param {Object =   null} fob File object where items will override dob's.
     * @param {string =   ''} p Prefix. Useful for wrapping objects and necessary for internal use (as this is a recursive method).
     */
    append(fd: FormData, dob: Object, fob: Object = null, p: string = ''){
        let apnd = this.append;

        function isObj(dob, fob, p){
            if(typeof dob == "object"){
                if(!!dob && dob.constructor === Array){
                    p += '[]';
                    for(let i = 0; i < dob.length; i++){
                        let aux_fob = !!fob ? fob[i] : fob;
                        isObj(dob[i], aux_fob, p);
                    }
                } else {
                    apnd(fd, dob, fob, p);
                }
            } else {
                let value = !!fob ? fob : dob;
                fd.append(p, value);
            }
        }

        for(let prop in dob){
            let aux_p = p == '' ? prop : `${p}[${prop}]`;
            let aux_fob = !!fob ? fob[prop] : fob;
            isObj(dob[prop], aux_fob, aux_p);
        }
    }

Ви повинні включити індекси масиву замість []для об'єктних властивостей всередині числового масиву, щоб залишатися недоторканими
Vicary

1

Версія TypeScript:

static convertModelToFormData(model: any, form: FormData = null, namespace = ''): FormData {
    let formData = form || new FormData();
    for (let propertyName in model) {
      if (!model.hasOwnProperty(propertyName) || model[propertyName] == undefined) continue;
      let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
      if (model[propertyName] instanceof Date) {        
        formData.append(formKey, this.dateTimeToString(model[propertyName]));
      }
      else if (model[propertyName] instanceof Array) {
        model[propertyName].forEach((element, index) => {
          if (typeof element != 'object')
            formData.append(`${formKey}[]`, element);
          else {
            const tempFormKey = `${formKey}[${index}]`;
            this.convertModelToFormData(element, formData, tempFormKey);
          }
        });
      }
      else if (typeof model[propertyName] === 'object' && !(model[propertyName] instanceof File)) {        
        this.convertModelToFormData(model[propertyName], formData, formKey);
      }
      else {        
        formData.append(formKey, model[propertyName].toString());
      }
    }
    return formData;
  }

https://gist.github.com/Mds92/091828ea857cc556db2ca0f991fee9f6


1
Перш за все, namespaceце зарезервоване ключове слово в TypeScript( typescriptlang.org/docs/handbook/namespaces.html і github.com/Microsoft/TypeScript/issues / ... ). Крім того, здається, ви забули розібратися з Fileостанньої elseволі append "[object File]"до formData.
Jyrkka

1

Ви можете просто встановити qs:

npm i qs

Просто імпортуйте:

import qs from 'qs'

Передати об’єкт qs.stringify():

var item = {
   description: 'Some Item',
   price : '0.00',
   srate : '0.00',
   color : 'red',
   ...
   ...
}

qs.stringify(item)

1

Рекурсивно

const toFormData = (f => f(f))(h => f => f(x => h(h)(f)(x)))(f => fd => pk => d => {
  if (d instanceof Object) {
    Object.keys(d).forEach(k => {
      const v = d[k]
      if (pk) k = `${pk}[${k}]`
      if (v instanceof Object && !(v instanceof Date) && !(v instanceof File)) {
        return f(fd)(k)(v)
      } else {
        fd.append(k, v)
      }
    })
  }
  return fd
})(new FormData())()

let data = {
  name: 'John',
  age: 30,
  colors: ['red', 'green', 'blue'],
  children: [
    { name: 'Max', age: 3 },
    { name: 'Madonna', age: 10 }
  ]
}
console.log('data', data)
document.getElementById("data").insertAdjacentHTML('beforeend', JSON.stringify(data))

let formData = toFormData(data)

for (let key of formData.keys()) {
  console.log(key, formData.getAll(key).join(','))
  document.getElementById("item").insertAdjacentHTML('beforeend', `<li>${key} = ${formData.getAll(key).join(',')}</li>`)
}
<p id="data"></p>
<ul id="item"></ul>


найкраща відповідь imho
ling

0

Цей метод перетворить об'єкт JS у FormData:

function convertToFormData(params) {
    return Object.entries(params)
        .reduce((acc, [key, value]) => {
            if (Array.isArray(value)) {
                value.forEach((v, k) => acc.append(`${key}[${k}]`, value));
            } else if (typeof value === 'object' && !(value instanceof File) && !(value instanceof Date)) {
                Object.entries(value).forEach((v, k) => acc.append(`${key}[${k}]`, value));
            } else {
                acc.append(key, value);
            }

            return acc;
        }, new FormData());
}


Просто зафіксуйте ітерацію вкладених записів об'єкта: Object.entries(value).forEach((v, k) => acc.append(`${key}[${v[0]}]`, v[1]));
heber gentilin

0

У моєму випадку мій об’єкт також мав властивість, яка представляла собою масив файлів. Оскільки вони є двійковими, їх слід розглядати інакше - індекс не повинен бути частиною ключа. Тож я змінив відповідь @Vladimir Novopashin та @ developer033:

export function convertToFormData(data, formData, parentKey) {
  if(data === null || data === undefined) return null;

  formData = formData || new FormData();

  if (typeof data === 'object' && !(data instanceof Date) && !(data instanceof File)) {
    Object.keys(data).forEach(key => 
      convertToFormData(data[key], formData, (!parentKey ? key : (data[key] instanceof File ? parentKey : `${parentKey}[${key}]`)))
    );
  } else {
    formData.append(parentKey, data);
  }

  return formData;
}

0

Я використовував це для розміщення даних моїх об’єктів у вигляді даних даних.

const encodeData = require('querystring');

const object = {type: 'Authorization', username: 'test', password: '123456'};

console.log(object);
console.log(encodeData.stringify(object));

0

Можливо, ви шукаєте саме цей код, який отримує ваш об’єкт javascript, створіть з нього об’єкт FormData, а потім розмістіть його на вашому сервері за допомогою нового API Fetch :

    let myJsObj = {'someIndex': 'a value'};

    let datos = new FormData();
    for (let i in myJsObj){
        datos.append( i, myJsObj[i] );
    }

    fetch('your.php', {
        method: 'POST',
        body: datos
    }).then(response => response.json())
        .then(objson => {
            console.log('Success:', objson);
        })
        .catch((error) => {
            console.error('Error:', error);
        });

0

Я посилаюся на це з відповіді Гудрейдена . Я трохи її редагую у форматі Typescript.

class UtilityService {
    private appendFormData(formData, data, rootName) {

        let root = rootName || '';
        if (data instanceof File) {
            formData.append(root, data);
        } else if (Array.isArray(data)) {
            for (var i = 0; i < data.length; i++) {
                this.appendFormData(formData, data[i], root + '[' + i + ']');
            }
        } else if (typeof data === 'object' && data) {
            for (var key in data) {
                if (data.hasOwnProperty(key)) {
                    if (root === '') {
                        this.appendFormData(formData, data[key], key);
                    } else {
                        this.appendFormData(formData, data[key], root + '.' + key);
                    }
                }
            }
        } else {
            if (data !== null && typeof data !== 'undefined') {
                formData.append(root, data);
            }
        }
    }

    getFormDataFromObj(data) {
        var formData = new FormData();

        this.appendFormData(formData, data, '');

        return formData;
    }
}

export let UtilityMan = new UtilityService();

0

Я можу трохи запізнитися на вечірку, але це те, що я створив для перетворення єдиного об'єкта у FormData.

function formData(formData, filesIgnore = []) {
  let data = new FormData();

  let files = filesIgnore;

  Object.entries(formData).forEach(([key, value]) => {
    if (typeof value === 'object' && !files.includes(key)) {
      data.append(key, JSON.stringify(value) || null);
    } else if (files.includes(key)) {
      data.append(key, value[0] || null);
    } else {
      data.append(key, value || null);
    }
  })

  return data;
}

Як це працює? Він перетворить та поверне всі очікувані властивості файлових об’єктів у списку ігнорування (2-й аргумент. Якщо хтось міг би сказати мені кращий спосіб визначити це, що допоможе!), У рядок json за допомогоюJSON.stringify . Тоді на вашому сервері вам просто потрібно буде перетворити його назад в об’єкт JSON.

Приклад:

let form = {
  first_name: 'John',
  last_name: 'Doe',
  details: {
    phone_number: 1234 5678 910,
    address: '123 Some Street',
  },
  profile_picture: [object FileList] // set by your form file input. Currently only support 1 file per property.
}

function submit() {
  let data = formData(form, ['profile_picture']);

  axios.post('/url', data).then(res => {
    console.log('object uploaded');
  })
}

Я як і раніше новачок у Http-запитах та JavaScript, тому будь-який відгук буде дуже вдячний!


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.