JSオブジェクトをフォームデータに変換


128

JSオブジェクトをどのように変換できFormDataますか?

私がこれをしたい理由は、〜100フォームのフィールド値から作成したオブジェクトがあるためです。

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

次に、アップロードファイルの機能をフォームに追加するように求められます。これはもちろん、JSONでは不可能なので、に移動することを計画していFormDataます。JSオブジェクトを変換できる方法はありますFormDataか?


作業/進捗状況を共有できますか?
リチケシュ2014

JSON.stringify()はどうですか?
Sunny Sharma 2014

1
@Sunny —文字列でJSONテキストを生成します。それはFormDataオブジェクトではありません。
クエンティン2014

はい、できます。formDataオブジェクトに追加できます。
adeneo 2014

FormDataの意味を教えてください。特定のフォーマット?
Sunny Sharma 2014

回答:


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- itemOPによって作成された通常のオブジェクトです。そのため、オブジェクトコンストラクターに何かをプロトタイプ化するという間違いを犯さない限り、それは独自のものではないプロパティを持つべきではありません。 、それは私たちが守らなければならないものではありません。
adeneo 2014年

2
@Lior-キーと値のペアをFormDataに追加するだけで、プロトタイププロパティを追加しても何も壊れObject.keysません。キーを配列として取得する必要がないので、使用しても問題は解決しません。値は、for..inループを使用する必要があります。
アデネオ

2
もちろん、あなたはサーバーが何を期待しているのかわかりません... JSでは問題があるため、ソリューションはObject.keys()である必要はなく、hasOwnProperty()である可能性もありますが、少なくとも警告である必要があります。
リオール2014年

3
@Lior-サーバーがPOSTリクエストで1つ以上のキーと値のペアを受信したときにサーバーが壊れた場合、それは間違っています。私は答えは結構だと思います、そしてそれを使用するために変更するつもりはありません、Object.keysまたはhasOwnProperty()オブジェクトが質問に投稿されているので、それらのどれも必要とすべきではありません。hasOwnPropertyプラグインなどで使用されることObjectがあるのは、一部のユーザーがコンストラクターに対して何を行うかわからないためです。ただし、ほとんどの場合、作成したオブジェクトの継承されたプロパティをテストする必要はないはずです。これは、あなたはおそらく何か悪いことをしています。
adeneo 2014年

5
@Lior次に空からストローで飛行機を作るつもりですか?空から食べ物を落とすより本物の飛行機が集まることを期待していますか?hasOwnPropertyチェックが使用される理由を理解することは重要です。誰かの(推測、Crockfordの)本を読んでもあまり遠くに行かず、仲間のSoメンバーを100回以上教育しようとしているため、物事は「ベストプラクティス」と見なされます評判とあなたが持っている答えの100倍の数もあなたのポイントをあまり助けません。また、プロトタイプを変更する新しいサードパーティのlibを1つ挙げてください。その投稿は別の時間のものです...
Benjamin

83

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に追加します

@ developer033のES6バージョン:

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

ナイスキープ
イット

とてもうまくいきます!ありがとうございました!私はまた、追加する必要がありました&& !(data instanceof Blob)、私の画像をアップロードするために私の場合
クレマンBaconnier

if(typeof data === 'object' && data!== null){が値がnullの場合に例外をスローしていたため追加しました
al000y

ES6バージョンの&& !(Array.isArray(data) && !data.length)場合、「if」条件で追加しないと、空の配列が削除されます。
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
配列、オブジェクト、ファイルをサポートする唯一の答え。
5

こんにちは、なぜファイルをルートに追加するのですか?子供にも追加できますか?
セドリックアーノールド2018

@CedricArnould誤解の可能性がありますが、メソッドは再帰的であるためformData.append(root, data)、書き込まれたとしても、ルートに追加されているわけではありません。
グドラダイン

不思議なことにサーバーで結果を受け取ったとき、私はあなたの答えを理解しました。私はファイルとデータのユニークなコレクションを持っています。しかし、私はファイルで名前がそれがどの子に接続しているかについての情報を与えているのを見ることができます。たぶん、問題は.Net Coreとそれがアップロードファイルをどのように管理するかに起因します。ご回答有難うございます。
セドリックアーノールド2018

これを使おうとしていますが、使用例はありません。ignoreListパラメータの予想される形式はかなり役に立ちます。
jpro

8

以下のようにJSON.stringify関数を試してください

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

1
これはこれを達成するための最良の方法です。
Rob

エラーデバッグを数回行った後、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);

form_dataの内容を抽出できないため、変換された結果を確認するためだけにtestJSONを使用しています。AJAXポストコール:

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

こんにちはラジ、配列はどうですか?複数の請求先住所があるとします。どのように修正しますか?
サム

1
答えを見つけました!あなたの投稿は本当に役に立ちます。ありがとう!
サム

3

遅い回答で申し訳ありませんが、Angular 2は現在ファイルのアップロードをサポートしていないため、私はこれに苦労していました。それで、それを行う方法はXMLHttpRequestwith を送信することでした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
まずnamespaceTypeScripttypescriptlang.org/docs/handbook/namespaces.htmlおよびgithub.com/Microsoft/TypeScript/issues/…)の予約済みキーワードです。また、あなたが対処するのを忘れているようだFile最後以来elseの意志append "[object File]"formData
Jyrkka、2018

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>


最良の答えの私見

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]));
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オブジェクトを作成し、そして新しいFetch APIを使用してそれをサーバーにPOST するコードです:

    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;
}

それはどのように機能しますか?これは、すべてのプロパティが無視リストで設定したFileオブジェクト(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に少し新しいので、どんなフィードバックも高く評価されます!


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.