如何将 JSON 字符串转换为 URL 参数(GET 请求)?

Posted

技术标签:

【中文标题】如何将 JSON 字符串转换为 URL 参数(GET 请求)?【英文标题】:How can I convert a JSON string to URL parameters (GET request)? 【发布时间】:2015-06-24 23:35:17 【问题描述】:

我有以下 JSON 必须转换为 GET 请求的 URL 参数。

一个example is given here,但是由于这个对象的复杂性,可以有多个line_items_attributes,每个都具有如图所示的给定值,我很难传递正确的值。

我也尝试仅序列化 JSON 对象并传递该值,但这也没有解决问题。


    "purchase_invoice":
    
        "date":"14/04/2015",
        "due_date":"14/04/2015",
        "contact_id":500,
        "contact_name":"TestContact",
        "reference":"TestReference",
        "line_items_attributes":[
            
                "unit_price":10.00,
                "quantity":1,
                "description":"TestLineItemAttDesc",
                "tax_code_id":1,
                "ledger_account_id":501,
                "tax_rate_percentage":19.0,
                "tax_amount":1.60

            ]
    

我已经搜索了一段时间,但运气不佳。任何见解都受到赞赏和欢迎!

这是调用的 API 不支持 JSON 格式的传入数据,因此无法在服务器端执行此操作或更改 Web 服务以支持 JSON 格式的数据。

【问题讨论】:

是你不知道最终的 URL 字符串应该是什么样子的问题,还是你只是不知道如何在 C# 中实现它? 问题是我不知道最终的 URL 应该是什么样子。基于 URL 示例,我可以尝试在 C# 中构建它:)。 一种方法是用 base64 对其进行编码,并用例如下划线替换“/”字符。否则,您可以像 @Bas 在他的回答中建议的那样对其进行 url 编码。 @AStopher - 您为此添加了赏金,但表单编码的有效负载是一系列名称/值对;在这种情况下,您希望如何表示复杂的分层对象(可能包括数组)?请参阅How do I use FormUrlEncodedContent for complex data types?,其中接受的答案指出,没有将多维键值结构转换为单维结构的约定或标准。 @AStopher - 但是如果您要问,我如何创建一个像已接受答案中所示的查询字符串,那么您可以使用 Json.NET 来做到这一点显示在这个小提琴中:dotnetfiddle.net/F90qLD。这就是你想要的吗? 【参考方案1】:

x-www-form-urlencoded 内容本质上是键/值元组的平面序列,正如 Tomalak 在this answer 到 How do I use FormUrlEncodedContent for complex data types? 中所解释的那样,没有规范的方法来转换将分层的嵌套键/值结构转换为平面结构。

尽管如此,从accepted answer 到这个问题,来自 Stripe API 的this example,以及上面提到的question,似乎通过将它们的键括在括号中来扁平化复杂嵌套对象内的参数和像这样将它们附加到最上面的键:


     "purchase_invoice[date]", "14/04/2015"  
     "purchase_invoice[due_date]", "14/04/2015"  
     "purchase_invoice[contact_id]", "500"  
     "purchase_invoice[contact_name]", "TestContact"  
     "purchase_invoice[reference]", "TestReference"  
     "purchase_invoice[line_items_attributes][0][unit_price]", "10"  
     "purchase_invoice[line_items_attributes][0][quantity]", "1"  
     "purchase_invoice[line_items_attributes][0][description]", "TestLineItemAttDesc"  
     "purchase_invoice[line_items_attributes][0][tax_code_id]", "1"  
     "purchase_invoice[line_items_attributes][0][ledger_account_id]", "501"  
     "purchase_invoice[line_items_attributes][0][tax_rate_percentage]", "19"  
     "purchase_invoice[line_items_attributes][0][tax_amount]", "1.6"  

如果这是您想要的,您可以使用以下扩展方法生成带有json.net 的此类键/值对:

public static partial class JsonExtensions

    public static string ToUrlEncodedQueryString(this JContainer container)
    
        return container.ToQueryStringKeyValuePairs().ToUrlEncodedQueryString();
    

    public static IEnumerable<KeyValuePair<string, string>> ToQueryStringKeyValuePairs(this JContainer container)
    
        return container.Descendants()
            .OfType<JValue>()
            .Select(v => new KeyValuePair<string, string>(v.ToQueryStringParameterName(), (string)v));
    

    public static string ToUrlEncodedQueryString(this IEnumerable<KeyValuePair<string, string>> pairs)
    
        return string.Join("&", pairs.Select(p => HttpUtility.UrlEncode(p.Key) + "=" + HttpUtility.UrlEncode(p.Value)));
        //The following works but it seems heavy to construct and await a task just to built a string:
        //return new System.Net.Http.FormUrlEncodedContent(pairs).ReadAsStringAsync().Result;
        //The following works and eliminates allocation of one intermediate string per pair, but requires more code:
        //return pairs.Aggregate(new StringBuilder(), (sb, p) => (sb.Length > 0 ? sb.Append("&") : sb).Append(HttpUtility.UrlEncode(p.Key)).Append("=").Append(HttpUtility.UrlEncode(p.Value))).ToString();
        //Answers from https://***.com/questions/3865975/namevaluecollection-to-url-query that use HttpUtility.ParseQueryString() are wrong because that class doesn't correctly escape the keys names.
    

    public static string ToQueryStringParameterName(this JToken token)
    
        // Loosely modeled on JToken.Path
        // https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L184
        // By https://github.com/JamesNK
        if (token == null || token.Parent == null)
            return string.Empty;
        var positions = new List<string>();
        for (JToken previous = null, current = token; current != null; previous = current, current = current.Parent)
        
            switch (current)
            
                case JProperty property:
                    positions.Add(property.Name);
                    break;
                case JArray array:
                case JConstructor constructor:
                    if (previous != null)
                        positions.Add(((IList<JToken>)current).IndexOf(previous).ToString(CultureInfo.InvariantCulture)); // Don't localize the indices!
                    break;
            
        
        var sb = new StringBuilder();
        for (var i = positions.Count - 1; i >= 0; i--)
        
            var name = positions[i];
            // TODO: decide what should happen if the name contains the characters `[` or `]`.
            if (sb.Length == 0)
                sb.Append(name);
            else
                sb.Append('[').Append(name).Append(']');
        

        return sb.ToString();
    

然后如果你有一个 JSON 字符串,你可以将它解析成一个 LINQ-to-JSON JObject 并像这样生成查询字符串:

var obj = JObject.Parse(jsonString);
var queryString = obj.ToUrlEncodedQueryString();

或者,如果您有一些分层数据模型 POCO,您可以使用 JObject.FromObject() 从模型生成您的 JObject

var obj = JObject.FromObject(myModel);
var queryString = obj.ToUrlEncodedQueryString();

演示小提琴here.

【讨论】:

【参考方案2】:

听起来你需要x-www-form-urlencoded

从您的示例中,它看起来像这样:

purchase_invoice%5Bdate%5D=14%2F04%2F2015&purchase_invoice%5Bdue_date%5D=14%2F04%2F2015&purchase_invoice%5Bcontact_id%5D=500&purchase_invoice%5Bcontact_name%5D=TestContact&purchase_invoice%5Breference%5D=TestReference&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bunit_price%5D=10&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bquantity%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bdescription%5D=TestLineItemAttDesc&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_code_id%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bledger_account_id%5D=501&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_rate_percentage%5D=19&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_amount%5D=1.6

据我所知,这种编码的最佳参考是 jQuery javascript 库中未记录的 jQuery.param 方法。

【讨论】:

这个答案真的没有帮助,因为它不能解决问题......【参考方案3】:

因此,使用任何URL Encoding mechanism 都可以轻松计算最终 URL。在 C# 中,我们可以执行以下操作:

string json = "...";
string baseUrl = "http://bla.com/somepage?myJson="
string urlWithJson = baseUrl + System.Net.WebUtility.UrlEncode(json)

有什么方法可以 POST 数据或以其他方式发送请求正文?它看起来会更容易/更清洁。

【讨论】:

以上是关于如何将 JSON 字符串转换为 URL 参数(GET 请求)?的主要内容,如果未能解决你的问题,请参考以下文章

Url的参数转变成json

ASIHttpRequest - 如何将参数的 NSDictionary 转换为 url 编码的查询参数字符串?

如何将大型 JSON 字符串转换为 JSON 对象?

提取url中参数的方法(转换成json格式)

js之JSON.stringify()使用详解

json 转换为struts url参数 json 转换为 url参数