如何将对象序列化为查询字符串格式?

Ben*_*min 61 asp.net serialization object query-string

如何将对象序列化为查询字符串格式?我似乎无法在谷歌上找到答案.谢谢.

这是我将序列化的对象作为示例.

public class EditListItemActionModel
{
    public int? Id { get; set; }
    public int State { get; set; }
    public string Prefix { get; set; }
    public string Index { get; set; }
    public int? ParentID { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ard 92

我99%肯定没有内置的实用方法.这不是一项非常常见的任务,因为Web服务器通常不会使用URLEncoded键/值字符串进行响应.

您如何看待混合反射和LINQ?这有效:

var foo = new EditListItemActionModel() {
  Id = 1,
  State = 26,
  Prefix = "f",
  Index = "oo",
  ParentID = null
};

var properties = from p in foo.GetType().GetProperties()
                 where p.GetValue(foo, null) != null
                 select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(foo, null).ToString());

// queryString will be set to "Id=1&State=26&Prefix=f&Index=oo"                  
string queryString = String.Join("&", properties.ToArray());
Run Code Online (Sandbox Code Playgroud)

更新:

要编写一个返回任何1-deep对象的QueryString表示的方法,您可以这样做:

public string GetQueryString(object obj) {
  var properties = from p in obj.GetType().GetProperties()
                   where p.GetValue(obj, null) != null
                   select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());

  return String.Join("&", properties.ToArray());
}

// Usage:
string queryString = GetQueryString(foo);
Run Code Online (Sandbox Code Playgroud)

您还可以将其作为扩展方法,而无需额外的工作

public static class ExtensionMethods {
  public static string GetQueryString(this object obj) {
    var properties = from p in obj.GetType().GetProperties()
                     where p.GetValue(obj, null) != null
                     select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());

    return String.Join("&", properties.ToArray());
  }
}

// Usage:
string queryString = foo.GetQueryString();
Run Code Online (Sandbox Code Playgroud)

  • 通过将每个属性的值分配给临时变量,例如使用`let value = p.GetValue(obj,null)`,可以使每个属性的值更有效. (2认同)
  • 这仅适用于服务器上的英语文化信息。如果您在 Windows 上设置特定的日期时间格式,这将不起作用,因为您应该将 CultureInfo Invariant 设置为 ToString 参数。同样的问题也存在于 float/double 中的特定小数分隔符上。 (2认同)

小智 15

基于其他评论的好主意,我制作了一个通用扩展方法.ToQueryString(),可以在任何对象上使用.

public static class UrlHelpers
{
    public static string ToQueryString(this object request, string separator = ",")
    {
        if (request == null)
            throw new ArgumentNullException("request");

        // Get all properties on the object
        var properties = request.GetType().GetProperties()
            .Where(x => x.CanRead)
            .Where(x => x.GetValue(request, null) != null)
            .ToDictionary(x => x.Name, x => x.GetValue(request, null));

        // Get names for all IEnumerable properties (excl. string)
        var propertyNames = properties
            .Where(x => !(x.Value is string) && x.Value is IEnumerable)
            .Select(x => x.Key)
            .ToList();

        // Concat all IEnumerable properties into a comma separated string
        foreach (var key in propertyNames)
        {
            var valueType = properties[key].GetType();
            var valueElemType = valueType.IsGenericType
                                    ? valueType.GetGenericArguments()[0]
                                    : valueType.GetElementType();
            if (valueElemType.IsPrimitive || valueElemType == typeof (string))
            {
                var enumerable = properties[key] as IEnumerable;
                properties[key] = string.Join(separator, enumerable.Cast<object>());
            }
        }

        // Concat all key/value pairs into a string separated by ampersand
        return string.Join("&", properties
            .Select(x => string.Concat(
                Uri.EscapeDataString(x.Key), "=",
                Uri.EscapeDataString(x.Value.ToString()))));
    }
}
Run Code Online (Sandbox Code Playgroud)

它也适用于具有Array类型和泛型Lists属性的对象(如果它们只包含基元或字符串).

试试看,欢迎评论:使用Reflection将对象序列化为查询字符串

  • 你为什么不把代码放在这里.它没那么多. (8认同)

yoe*_*alb 13

Json.Net通过序列化然后反序列化为键值对,使用它会容易得多。

这是一个代码示例:

using Newtonsoft.Json;
using System.Web;

string ObjToQueryString(object obj)
{
     var step1 = JsonConvert.SerializeObject(obj);

     var step2 = JsonConvert.DeserializeObject<IDictionary<string, string>>(step1);

     var step3 = step2.Select(x => HttpUtility.UrlEncode(x.Key) + "=" + HttpUtility.UrlEncode(x.Value));

     return string.Join("&", step3);
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这种简单性。它非常适合平面对象,但不适合嵌套对象/列表 (3认同)
  • 这也适用于 System.Text.Json 库,只需确保步骤 2 中的 IDictionary 为“&lt;string, object&gt;”,然后在步骤 3 中使用“x.Value.ToString()”即可。 (2认同)

Alv*_*vis 8

基于流行的答案,我需要更新代码以支持数组.分享实施:

public string GetQueryString(object obj)
{
    var result = new List<string>();
    var props = obj.GetType().GetProperties().Where(p => p.GetValue(obj, null) != null);
    foreach (var p in props)
    {
        var value = p.GetValue(obj, null);
        var enumerable = value as ICollection;
        if (enumerable != null)
        {
            result.AddRange(from object v in enumerable select string.Format("{0}={1}", p.Name, HttpUtility.UrlEncode(v.ToString())));
        }
        else
        {
            result.Add(string.Format("{0}={1}", p.Name, HttpUtility.UrlEncode(value.ToString())));
        }
    }

    return string.Join("&", result.ToArray());
}
Run Code Online (Sandbox Code Playgroud)


gio*_*i02 5

它对于嵌套对象也很有用

public static class HttpQueryStrings
{
    private static readonly StringBuilder _query = new();

    public static string ToQueryString<T>(this T @this) where T : class
    {
        _query.Clear();

        BuildQueryString(@this, "");

        if (_query.Length > 0) _query[0] = '?';

        return _query.ToString();
    }

    private static void BuildQueryString<T>(T? obj, string prefix = "") where T : class
    {
        if (obj == null) return;

        foreach (var p in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (p.GetValue(obj, Array.Empty<object>()) != null)
            {
                var value = p.GetValue(obj, Array.Empty<object>());


                if (p.PropertyType.IsArray && value?.GetType() == typeof(DateTime[]))
                    foreach (var item in (DateTime[])value)
                        _query.Append($"&{prefix}{p.Name}={item.ToString("yyyy-MM-dd")}");

                else if (p.PropertyType.IsArray)
                    foreach (var item in (Array)value!)
                        _query.Append($"&{prefix}{p.Name}={item}");

                else if (p.PropertyType == typeof(string))
                    _query.Append($"&{prefix}{p.Name}={value}");

                else if (p.PropertyType == typeof(DateTime) && !value!.Equals(Activator.CreateInstance(p.PropertyType))) // is not default 
                    _query.Append($"&{prefix}{p.Name}={((DateTime)value).ToString("yyyy-MM-dd")}");

                else if (p.PropertyType.IsValueType && !value!.Equals(Activator.CreateInstance(p.PropertyType))) // is not default 
                    _query.Append($"&{prefix}{p.Name}={value}");


                else if (p.PropertyType.IsClass)
                    BuildQueryString(value, $"{prefix}{p.Name}.");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用该解决方案的示例:

string queryString = new
{
    date = new DateTime(2020, 1, 1),
    myClass = new MyClass
    {
        FirstName = "john",
        LastName = "doe"
    },
    myArray = new int[] { 1, 2, 3, 4 },
}.ToQueryString();
Run Code Online (Sandbox Code Playgroud)