将多维对象转换为查询字符串

3

我有一个对象:

{"f":{"cid":"325","field_name[10][0]":"133","field_name[10][1]":"132","price":"320|3600"}}

我想将这个对象转换为查询字符串。 我正在使用这个函数:

function toQueryString(obj, prefix) {
        var str = [];
        for(var p in obj) {
            var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
            str.push(typeof v == "object" ?
              toQueryString(v, k) :
              encodeURIComponent(k) + "=" + encodeURIComponent(v));
        }
        return str.join("&");
}

但是这个函数给我返回的结果是:
f[cid]=325&f[field_name[10][0]]=133&f[field_name[10][1]]=132&f[price]=320%7C3600

我在服务器端无法得到正确的结果,这是错误的:

Array
(
    [f] => Array
        (
            [cid] => 325
            [field_name[10] => Array
                (
                    [0] => 133
                )

            [price] => 320|3600
        )
)

我该如何解决这个问题? 我认为正确的结果应该类似于这样:

f[cid]=325&f[field_name[[10][0]]]=133&f[field_name[[10][1]]]=132&f[price]=320%7C3600

1
你想要什么样的输出? - MeLight
2
我建议(除非受到限制)将其作为urlencoded json传递。这样可能会更容易做到正确。但是在php端必须显式解码json。 - Tim Seguine
如果不行的话,我记得有一个jQuery插件可以做到这一点。我想它叫做“Serialize object”。 - Tim Seguine
我需要一个纯JavaScript的解决方案。谢谢。 - XTRUST.ORG
2
正如@TimSeguine建议的那样,如果需要纯JavaScript解决方案,您可以使用JSON.stringify()函数。 - MeLight
显示剩余3条评论
5个回答

9

我稍微修改了你的函数,以纠正嵌套的查询字符串:

function toQueryString(obj, prefix) {
    var str = [], k, v;
    for(var p in obj) {
        if (!obj.hasOwnProperty(p)) {continue;} // skip things from the prototype
        if (~p.indexOf('[')) {
            k = prefix ? prefix + "[" + p.substring(0, p.indexOf('[')) + "]" + p.substring(p.indexOf('[')) : p;
// only put whatever is before the bracket into new brackets; append the rest
        } else {
            k = prefix ? prefix + "[" + p + "]" : p;
        }
        v = obj[p];
        str.push(typeof v == "object" ?
          toQueryString(v, k) :
          encodeURIComponent(k) + "=" + encodeURIComponent(v));
    }
    return str.join("&");
}

现在运行此函数将为您生成以下查询字符串:
f[cid]=325&f[field_name][10][0]=133&f[field_name][10][1]=132&f[price]=320|3600

如果我们将它传递给一个PHP页面,并告诉它打印print_r($_GET),它会给我们返回:
Array
(
    [f] => Array
        (
            [cid] => 325
            [field_name] => Array
                (
                    [10] => Array
                        (
                            [0] => 133
                            [1] => 132
                        )

                )

            [price] => 320|3600
        )

)

这正是您想要的,对吗?


在使用 in 时,通常被认为是良好的编程习惯去检查 hasOwnProperty,以防止有人修改了原型。 - Tim Seguine

1
这个问题已经有一个可以满足你要求的答案,但我决定解释一下我的评论中提到的替代方法,因为我认为了解多种做法是好的。它不完全满足你的需求,但我认为这是一种更简单、更健壮、更易维护的传输任意对象的方法。
function toQueryString(obj, name)  {
    return encodeURIComponent(name) + '=' +
        encodeURIComponent(JSON.stringify(obj));
} 

在PHP端(假设name"foo"),你所需要做的就是:
$foo=json_decode($_GET["foo"], true);

唯一的困难在于,如果您想支持某些版本的Internet Explorer,则必须使用JSON.stringify的polyfill,但这些很容易获得。
缺点自然是额外的间接性,但我认为有一个重要的好处:您将此错误测试的负担推给了浏览器制造商和(如果必要)您决定使用的JSON.stringify实现的开发人员。
当然,这有引入新依赖项的缺点,但我认为它足够有用以证明其价值。
你的体验可能会有所不同。
另外:如果您可以使用POST请求发送数据,则可以直接将JSON.stringify的结果作为原始POST数据发送,而无需对其进行URL编码。在PHP端,您可以使用json_decode(file_get_contents("php://input"), true)获取它。

只有在您愿意更改 PHP 端的默认行为时,此方法才有效。我认为 PHP 代码不应该担心数据如何呈现给它。 - mr1031011
@mr1031011 默认行为没有标准化,不够健壮,也不容易在客户端支持。我同意你的看法,因为这并不是理想的解决方案。但是,在我看来,如果您完全控制您的堆栈,这是最容易支持健壮的方法。虽然被接受的解决方案并不差,但对于某些类型的数据存在一些问题。这是一个纯粹实用的解决方案,当时我遇到了类似于被接受答案引起的错误时,就想出了这个方法来处理它。 - Tim Seguine
注意:我现在只从事系统工作,所以不能自信地说我仍然会个人推荐我的解决方案。当我编写它时,它满足了我的需求,并且比我发现的任何其他替代方案都更简单。 - Tim Seguine
我同意你的观点,这是一个好的解决方案,当开发人员可以访问前端和后端代码时可以使用 :) - mr1031011

0

我刚刚写了这个。对我来说有效。

let objToQueryString = function (obj, prefix) {
    let fields = [];
    if (obj && Object.keys(obj).length) {
        for (let i in obj) {
            if (obj.hasOwnProperty(i)) {
                if (typeof obj[i] === 'object') {
                    fields.push(objToQueryString(obj[i], prefix ? prefix + '[' + i + ']' : i));
                } else {
                    fields.push((prefix ? prefix + '[' + i + ']' : i) + '=' + (typeof obj[i] === 'undefined' ? '' : encodeURIComponent(obj[i])));
                }
            }
        }
    } else if (prefix) {
        fields.push(prefix + '=');
    }
    return fields.join('&');
};

objToQueryString({x: 1, y: 2, z: {a: 10, b: 20, c: {i: 100, j: null, k: {}}}}); //returns "x=1&y=2&z[a]=10&z[b]=20&z[c][i]=100&z[c][j]=&z[c][k]="

请享受。


0

类型Script

declare global {
    interface Object {
        queryString: () => string;
    }
}    

if (!Object.prototype.queryString) {
    Object.defineProperty(Object.prototype, 'queryString', {
        value: function () {
            var parts = [];
            let obj = this;
            for (var key in obj) {
                if (typeof obj[key] == 'object') {
                    for (var key2 in obj[key]) {
                        parts.push(`${encodeURIComponent(key)}[${encodeURIComponent(key2)}]` + '=' + encodeURIComponent(obj[key][key2]));
                    }
                } else if (typeof obj[key] == 'string') {
                    parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
                }
            }
            return "?" + parts.join('&');
        }
    });
}

input: {"test":{"convert":"querystring"}}
output: test[convert]=querystring

-2
function solution(inputString, objectName)
{
    var startingIndex =  inputString.replace(/'/g, "").indexOf('{')
    var endingIndex =  inputString.replace(/'/g, "").indexOf('}')
    var propertyValuePairs = inputString.replace(/'/g, "").substring(startingIndex + 1, endingIndex ).split(',')
    var propertyValues = new Array();

    $.each(propertyValuePairs , function(index, element){ 
        var elements = element.split(':'); propertyValues.push({property: elements[0], value: elements[1]}); 
    });

    var result = "";

    for (var i = 0; i < propertyValues.length; i++) {
         result += objectName + "[" + propertyValues[i].property + "]" + "=" + propertyValues[i].value + "&";
    }
    return result.substring(0, result.length - 1);
}

好的,没问题 :-) 我确实没有花太多功夫在命名上,我粘贴的代码版本并没有运行,因为我匆忙地把它粘贴到这里(代码是从我的控制台实验中进行复制和粘贴构建的)。现在它可以运行并返回了预期的结果。至于“它并不总是有效”-它可以适用于OP提出的场景 ;) - Veverke
当我修正名称后进行测试,它返回与原帖相同的结果,这不是他想要的。 - Scimonster
它唯一的缺点就是,正如我所说,它被硬编码为仅适用于单个对象。我可以使解码更好,以便在字符串中传递多个对象,但这超出了所提出问题的范围,因此我没有这样做。 - Veverke
问题出在周围的括号上。它们防止深度数组正确解析。另外,为什么你要将我的答案引用为 OP? - Scimonster
我明白了。无论如何,我现在才注意到这个问题已经解决了。所以这次不用我了。谢谢。 - Veverke
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接