如何将 JSON 字符串转换为 URL 参数(GET 请求)?
How can I convert a JSON string to URL parameters (GET request)?
我有以下 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
}]
}
}
我已经搜索了一段时间,但运气不佳。任何见解都将受到赞赏和欢迎!
这是在调用不支持 JSON 格式的传入数据的 API,因此在服务器端执行此操作或更改 Web 服务以支持 JSON 格式的数据不可能。
所以最后的 URL 可以很容易地使用任何 URL Encoding mechanism 来计算。在 C# 中,我们可以执行以下操作:
string json = "...";
string baseUrl = "http://bla.com/somepage?myJson="
string urlWithJson = baseUrl + System.Net.WebUtility.UrlEncode(json)
有什么方法可以 POST 数据或以其他方式发送请求正文吗?好像有点easier/cleaner.
听起来你需要的东西是 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
方法。
x-www-form-urlencoded
content is, essentially, a flat sequence of key/value tuples, and as explained in this answer to How do I use FormUrlEncodedContent for complex data types? by Tomalak,没有规范的方法可以将分层嵌套 key/value 结构转换为平面结构。
然而,从上面提到的 to this question, this example from the Stripe API, and the 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" }
}
如果这是您想要的,您可以使用以下扩展方法生成这样的 key/value 与 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 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();
演示 fiddle here.
我有以下 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
}]
}
}
我已经搜索了一段时间,但运气不佳。任何见解都将受到赞赏和欢迎!
这是在调用不支持 JSON 格式的传入数据的 API,因此在服务器端执行此操作或更改 Web 服务以支持 JSON 格式的数据不可能。
所以最后的 URL 可以很容易地使用任何 URL Encoding mechanism 来计算。在 C# 中,我们可以执行以下操作:
string json = "...";
string baseUrl = "http://bla.com/somepage?myJson="
string urlWithJson = baseUrl + System.Net.WebUtility.UrlEncode(json)
有什么方法可以 POST 数据或以其他方式发送请求正文吗?好像有点easier/cleaner.
听起来你需要的东西是 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
方法。
x-www-form-urlencoded
content is, essentially, a flat sequence of key/value tuples, and as explained in this answer to How do I use FormUrlEncodedContent for complex data types? by Tomalak,没有规范的方法可以将分层嵌套 key/value 结构转换为平面结构。
然而,从上面提到的
{
{ "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" }
}
如果这是您想要的,您可以使用以下扩展方法生成这样的 key/value 与 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 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();
演示 fiddle here.