我可以想出几种方法来实现这一点。我将向您展示一些结合了三个的代码,以便您选择您喜欢的一个。
在所有情况下,我们都是在将值应用于您的 serializeObject 方法之前在对象内创建路径。为此,我添加了一个可以执行此操作的递归私有方法。解决了这个问题,唯一的问题是如何确定您的价值的最终路径,这就是我找到三种不同方法的地方:
-
将输入名称写入所需路径,如:
<input name="some.path" />
-
使用对象中的属性来形成路径。我在示例中使用了 data-* 属性,但您可以轻松更改为使用 id。例如,这将导致 JSON 对象中 region->idRegion 的映射:
<select name="region" data-form-path="idRegion">
-
创建映射对象以确定如何将输入名称映射到对象路径,例如:
var mapping = {
"country" : "location.country",
"isForeignCountry" : "location.isForeignCountry"
}
所以,这里有一个示例表单,我们可以使用它来演示它的各种工作方式:
<form>
<input type="text" name="name" value="1"/>
<input type="text" name="lasntame" value="2"/>
<input type="text" name="photos.total" value="2"/>
<input type="text" name="photo.items" value="face.jpg" id="b"/>
<input type="text" name="photo.items" value="landscape.jpg" id="b"/>
<textarea name="userData.comments" data-form-path="1355998"></textarea>
<select name="region" data-form-path="idRegion">
<option value="5" selected="selected">5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
<select name="country" >
<option value="5" selected="selected">5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
<input type="checkbox" checked="checked" name="isForeignCountry" value="8" id="f">
<input type="submit" name="check" value="Submit" id="g">
</form>
如您所见,我们有名为“photos.total”和“photos.items”的输入。有两个“项目”,因此您可以看到多个值字段仍然可以正确转换。这些使用第一种方法来映射到最终的值路径。
我们还有两个带有 data-form-path 属性的项目(顺便说一下,一个 HTML5 有效的 data-* 属性)。这些将使用第二种方法,对于加分,textarea 的名称中有一个路径,因此它也使用第一种方法。
修改后的代码如下:
$.fn.serializeObject = function(mapping)
{
var o = {};
var a = this.serializeArray();
var that = this;
$.each(a, function() {
var name = this.name;
// We store current reference in a variable because when a nested path
// is created, the value is set on the last element of the path,
// instead of the JSON root
var currentRef = o;
if(!mapping) {
// 1st way: using names with path, like 'inputname="location.latitude" '
if(name.indexOf('.') != -1) {
var path = name.split('.');
currentRef = createPath(o, path);
name = name.split('.').splice(-1,1);
}
// Second way: adding metadata (this could be simply the id instead)
// Bonus! supports also paths with dots
var idData = $("*[name='" + this.name + "']",that).data("form-path");
if(idData){
currentRef = createPath(currentRef, [name,idData]);
name = idData;
}
}
else {
// Final way: simply use a mapping between form inputs and json path
if(mapping[name]){
var path = mapping[name].split('.');
currentRef = createPath(o, path);
name = name.split('.').splice(-1,1);
}
}
if (currentRef[name] !== undefined) {
if (!currentRef[name].push) {
currentRef[name] = [currentRef[name]];
}
currentRef[name].push(this.value || '');
} else {
currentRef[name] = this.value || '';
}
});
// Recursively create a path in the obj item and return
// the last created object reference
function createPath(obj, pathArray) {
var item = pathArray.splice(0,1);
var returnObj;
if(pathArray.length === 0) {
returnObj = obj;
}
else if(!obj[item]) {
obj[item] = {};
returnObj = createPath(obj[item], pathArray);
}
else returnObj = createPath(obj[item], pathArray);
return returnObj;
}
return o;
};
现在,如果我们在没有映射的情况下进行调用,这个表单的结果是:
{
"name":"1",
"lasntame":"2",
"photo":{
"total":"2",
"items":[
"face.jpg",
"landscape.jpg"]
}
,"userData":{
"comments":{
"1355998":""
}
},
"region":{
"idRegion":"5"
},
"country":"5",
"isForeignCountry":"8"
}
名称为“photos.*”的输入已放置在与其名称对应的路径中。并且具有 data-form-path 属性的输入也正确缩进。
如果修改 HTML 不是一个选项(让我说您在询问时需要更具体!),您可以使用输入映射对象。所以如果我这样打电话:
var mapping = {
"country" : "location.country",
"isForeignCountry" : "location.isForeignCountry"
}
console.log( $("form").serializeObject(mapping) );
使用对象映射而不是其他规则,导致:
{
"name":"1",
"lasntame":"2",
"photo.total":"2",
"photo.items":[
"face.jpg",
"landscape.jpg"],
"userData.comments":"",
"region":"5",
"location":{
"country":"5",
"isForeignCountry":"8"
}
}
我想这应该可以满足您的需求。我希望你问得更详细一些,比如显示你的 HTML 并解释你可以改变什么或不能改变什么。让我知道这是否对您有帮助。干杯!