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

3 c# json url-parameters

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

这里给出了一个例子,但是由于这个对象的复杂性,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

            }]
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经搜索了一段时间但没有太多运气。任何见解都值得赞赏和欢迎!

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

dbc*_*dbc 7

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

然而,从这个问题的公认答案、 Stripe API 的这个例子以及上面提到的问题来看,似乎通过将参数放在括号中并将它们附加到最上面的键来展平复杂嵌套对象内的参数是很常见的所以:

{
    { "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" } 
}
Run Code Online (Sandbox Code Playgroud)

如果这是您想要的,您可以使用以下扩展方法通过

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 /sf/ask/270618281/ 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,如果您有 JSON 字符串,则可以将其解析为LINQ-to-JSON JObject并生成查询字符串,如下所示:

var obj = JObject.Parse(jsonString);
var queryString = obj.ToUrlEncodedQueryString();
Run Code Online (Sandbox Code Playgroud)

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

var obj = JObject.FromObject(myModel);
var queryString = obj.ToUrlEncodedQueryString();
Run Code Online (Sandbox Code Playgroud)

演示小提琴在这里