개체를 쿼리 문자열 형식으로 직렬화하는 방법은 무엇입니까?
개체를 쿼리 문자열 형식으로 직렬화하는 방법은 무엇입니까? Google에서 답을 찾을 수없는 것 같습니다. 감사.
예를 들어 직렬화 할 객체는 다음과 같습니다.
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; }
}
나는 이것에 대한 내장 유틸리티 방법이 없다고 99 % 확신합니다. 웹 서버는 일반적으로 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());
최신 정보:
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);
추가 작업없이 확장 방법으로 만들 수도 있습니다.
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();
다른 주석의 좋은 아이디어를 바탕으로 모든 객체에 사용할 수있는 일반 확장 메서드 .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()))));
}
}
또한 기본 요소 또는 문자열 만 포함하는 경우 배열 유형 및 일반 목록의 속성을 가진 객체에 대해서도 작동합니다.
그것을 시도해보십시오, 주석은 환영합니다 : Reflection을 사용하여 객체를 쿼리 문자열로 직렬화하십시오 .
인기있는 답변을 바탕으로 배열을 지원하도록 코드를 업데이트해야했습니다. 구현 공유 :
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());
}
이 내 솔루션 :
public static class ObjectExtensions
{
public static string ToQueryString(this object obj)
{
if (!obj.GetType().IsComplex())
{
return obj.ToString();
}
var values = obj
.GetType()
.GetProperties()
.Where(o => o.GetValue(obj, null) != null);
var result = new QueryString();
foreach (var value in values)
{
if (!typeof(string).IsAssignableFrom(value.PropertyType)
&& typeof(IEnumerable).IsAssignableFrom(value.PropertyType))
{
var items = value.GetValue(obj) as IList;
if (items.Count > 0)
{
for (int i = 0; i < items.Count; i++)
{
result = result.Add(value.Name, ToQueryString(items[i]));
}
}
}
else if (value.PropertyType.IsComplex())
{
result = result.Add(value.Name, ToQueryString(value));
}
else
{
result = result.Add(value.Name, value.GetValue(obj).ToString());
}
}
return result.Value;
}
private static bool IsComplex(this Type type)
{
var typeInfo = type.GetTypeInfo();
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// nullable type, check if the nested type is simple.
return IsComplex(typeInfo.GetGenericArguments()[0]);
}
return !(typeInfo.IsPrimitive
|| typeInfo.IsEnum
|| type.Equals(typeof(Guid))
|| type.Equals(typeof(string))
|| type.Equals(typeof(decimal)));
}
}
이 확장 기능을 통합 테스트에 사용합니다. 완벽하게 작동합니다. :)
public static class UrlHelper
{
public static string ToUrl(this Object instance)
{
var urlBuilder = new StringBuilder();
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < properties.Length; i++)
{
urlBuilder.AppendFormat("{0}={1}&", properties[i].Name, properties[i].GetValue(instance, null));
}
if (urlBuilder.Length > 1)
{
urlBuilder.Remove(urlBuilder.Length - 1, 1);
}
return urlBuilder.ToString();
}
}
위의 또 다른 변형이지만 모델 클래스의 기존 DataMember 속성을 활용하고 싶었으므로 직렬화하려는 속성 만 GET 요청의 URL로 서버로 전송됩니다.
public string ToQueryString(object obj)
{
if (obj == null) return "";
return "?" + string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(DataMemberAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{p.Name}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
여기 당신이 필요로하는 일을하는 내가 쓴 글이 있습니다.
public string CreateAsQueryString(PageVariables pv) //Pass in your EditListItemActionModel instead
{
int i = 0;
StringBuilder sb = new StringBuilder();
foreach (var prop in typeof(PageVariables).GetProperties())
{
if (i != 0)
{
sb.Append("&");
}
var x = prop.GetValue(pv, null).ToString();
if (x != null)
{
sb.Append(prop.Name);
sb.Append("=");
sb.Append(x.ToString());
}
i++;
}
Formating encoding = new Formating();
// I am encoding my query string - but you don''t have to
return "?" + HttpUtility.UrlEncode(encoding.RC2Encrypt(sb.ToString()));
}
내가 한 것과 비슷한 상황에 직면하면 XML이 객체를 직렬화하고 쿼리 문자열 매개 변수로 전달하는 것입니다. 이 접근 방식의 어려움은 인코딩에도 불구하고 수신 양식에서 "잠재적으로 위험한 요청 ..."이라는 예외가 발생한다는 것입니다. 내가 해결 한 방법은 직렬화 된 객체를 암호화 한 다음이를 인코딩하여 쿼리 문자열 매개 변수로 전달하는 것이 었습니다. 결과적으로 쿼리 문자열 변조 방지 (HMAC 영역으로 방황하는 보너스)를 만들었습니다!
FormA XML은 객체 직렬화> 직렬화 된 문자열 암호화> 인코딩> FormB에 쿼리 문자열로 전달 FormB는 쿼리 매개 변수 값을 해독 (request.querystring도 디코딩 함)> XmlSerializer를 사용하여 결과 XML 문자열을 객체로 역 직렬화합니다.
어떻게 Ididit-at-applecart-dot-net에 요청하면 VB.NET 코드를 공유 할 수 있습니다.
Windows 10 (UWP) 앱에 대한 해결책을 찾고있었습니다. Dave가 제안한 Relection 접근 방식을 취하고 Microsoft.AspNet.WebApi.Client Nuget 패키지를 추가 한 후 속성 값의 Url Encoding을 처리하는 다음 코드를 사용했습니다.
private void AddContentAsQueryString(ref Uri uri, object content)
{
if ((uri != null) && (content != null))
{
UriBuilder builder = new UriBuilder(uri);
HttpValueCollection query = uri.ParseQueryString();
IEnumerable<PropertyInfo> propInfos = content.GetType().GetRuntimeProperties();
foreach (var propInfo in propInfos)
{
object value = propInfo.GetValue(content, null);
query.Add(propInfo.Name, String.Format("{0}", value));
}
builder.Query = query.ToString();
uri = builder.Uri;
}
}
이러한 메서드는이 속성의 왕과 함께 작동하지 않습니다.
public List<int?> list
목록 속성을 지원하는 간단한 접근 방식 :
public static class UriBuilderExtensions
{
public static UriBuilder SetQuery<T>(this UriBuilder builder, T parameters)
{
var fragments = typeof(T).GetProperties()
.Where(property => property.CanRead)
.Select(property => new
{
property.Name,
Value = property.GetMethod.Invoke(parameters, null)
})
.Select(pair => new
{
pair.Name,
List = (!(pair.Value is string) && pair.Value is IEnumerable list ? list.Cast<object>() : new[] { pair.Value })
.Select(element => element?.ToString())
.Where(element => !string.IsNullOrEmpty(element))
})
.Where(pair => pair.List.Any())
.SelectMany(pair => pair.List.Select(value => Uri.EscapeDataString(pair.Name) + '=' + Uri.EscapeDataString(value)));
builder.Query = string.Join("&", fragments);
return builder;
}
}
코드를 작성하여 각 유형을 직렬화하는 것만 큼 빠른 솔루션 :
public static class UriBuilderExtensions
{
public static UriBuilder SetQuery<TSource>(this UriBuilder builder, TSource parameters)
{
var fragments = Cache<TSource>.Properties
.Select(property => new
{
property.Name,
List = property.FetchValue(parameters)?.Where(item => !string.IsNullOrEmpty(item))
})
.Where(parameter => parameter.List?.Any() ?? false)
.SelectMany(pair => pair.List.Select(item => Uri.EscapeDataString(pair.Name) + '=' + Uri.EscapeDataString(item)));
builder.Query = string.Join("&", fragments);
return builder;
}
/// <summary>
/// Caches dynamically emitted code which converts a types getter property values to a list of strings.
/// </summary>
/// <typeparam name="TSource">The type of the object being serialized</typeparam>
private static class Cache<TSource>
{
public static readonly IEnumerable<IProperty> Properties =
typeof(TSource).GetProperties()
.Where(propertyInfo => propertyInfo.CanRead)
.Select(propertyInfo =>
{
var source = Expression.Parameter(typeof(TSource));
var getter = Expression.Property(source, propertyInfo);
var cast = Expression.Convert(getter, typeof(object));
var expression = Expression.Lambda<Func<TSource, object>>(cast, source).Compile();
return new Property
{
Name = propertyInfo.Name,
FetchValue = typeof(IEnumerable).IsAssignableFrom(propertyInfo.PropertyType) && propertyInfo.PropertyType != typeof(string) ?
CreateListFetcher(expression) :
CreateValueFetcher(expression)
};
})
.OrderBy(propery => propery.Name)
.ToArray();
/// <summary>
/// Creates a function which serializes a <see cref="IEnumerable"/> property value to a list of strings.
/// </summary>
/// <param name="get">A lambda function which retrieves the property value from a given source object.</param>
private static Func<TSource, IEnumerable<string>> CreateListFetcher(Func<TSource, object> get)
=> obj => ((IEnumerable)get(obj))?.Cast<object>().Select(item => item?.ToString());
/// <summary>
/// Creates a function which serializes a <see cref="object"/> property value to a list of strings.
/// </summary>
/// <param name="get">A lambda function which retrieves the property value from a given source object.</param>
private static Func<TSource, IEnumerable<string>> CreateValueFetcher(Func<TSource, object> get)
=> obj => new[] { get(obj)?.ToString() };
public interface IProperty
{
string Name { get; }
Func<TSource, IEnumerable<string>> FetchValue { get; }
}
private class Property : IProperty
{
public string Name { get; set; }
public Func<TSource, IEnumerable<string>> FetchValue { get; set; }
}
}
}
두 솔루션 사용의 예 :
var url = new UriBuilder("test.com").SetQuerySlow(new
{
Days = new[] { WeekDay.Tuesday, WeekDay.Wednesday },
Time = TimeSpan.FromHours(14.5),
Link = "conferences.com/apple/stream/15",
Pizzas = default(int?)
}).Uri;
출력 :
http://test.com/Days=Tuesday&Days=Wednesday&Time=14:30:00&Link=conferences.com%2Fapple%2Fstream%2F15
두 솔루션 모두 이국적인 유형, 색인화 된 매개 변수 또는 중첩 매개 변수를 처리하지 않습니다.
수동 직렬화가 더 간단한 경우이 c # 7 / .net4.7 접근 방식이 도움이 될 수 있습니다.
public static class QueryParameterExtensions
{
public static UriBuilder SetQuery(this UriBuilder builder, params (string Name, object Obj)[] parameters)
{
var list = parameters
.Select(parameter => new
{
parameter.Name,
Values = SerializeToList(parameter.Obj).Where(value => !string.IsNullOrEmpty(value))
})
.Where(parameter => parameter.Values.Any())
.SelectMany(parameter => parameter.Values.Select(item => Uri.EscapeDataString(parameter.Name) + '=' + Uri.EscapeDataString(item)));
builder.Query = string.Join("&", list);
return builder;
}
private static IEnumerable<string> SerializeToList(object obj)
{
switch (obj)
{
case string text:
yield return text;
break;
case IEnumerable list:
foreach (var item in list)
{
yield return SerializeToValue(item);
}
break;
default:
yield return SerializeToValue(obj);
break;
}
}
private static string SerializeToValue(object obj)
{
switch (obj)
{
case bool flag:
return flag ? "true" : null;
case byte number:
return number == default(byte) ? null : number.ToString();
case short number:
return number == default(short) ? null : number.ToString();
case ushort number:
return number == default(ushort) ? null : number.ToString();
case int number:
return number == default(int) ? null : number.ToString();
case uint number:
return number == default(uint) ? null : number.ToString();
case long number:
return number == default(long) ? null : number.ToString();
case ulong number:
return number == default(ulong) ? null : number.ToString();
case float number:
return number == default(float) ? null : number.ToString();
case double number:
return number == default(double) ? null : number.ToString();
case DateTime date:
return date == default(DateTime) ? null : date.ToString("s");
case TimeSpan span:
return span == default(TimeSpan) ? null : span.ToString();
case Guid guid:
return guid == default(Guid) ? null : guid.ToString();
default:
return obj?.ToString();
}
}
}
사용 예 :
var uri = new UriBuilder("test.com")
.SetQuery(("days", standup.Days), ("time", standup.Time), ("link", standup.Link), ("pizzas", standup.Pizzas))
.Uri;
Perhaps this Generic approach will be useful:
public static string ConvertToQueryString<T>(T entity) where T: class
{
var props = typeof(T).GetProperties();
return $"?{string.Join('&', props.Where(r=> r.GetValue(entity) != null).Select(r => $"{HttpUtility.UrlEncode(r.Name)}={HttpUtility.UrlEncode(r.GetValue(entity).ToString())}"))}";
}
Using Json.Net
it would be much easier, by serializing and then deserializing to key value pairs.
Here is a code example:
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);
}
참고URL : https://stackoverflow.com/questions/6848296/how-do-i-serialize-an-object-into-query-string-format
'development' 카테고리의 다른 글
이메일 주소에 영숫자가 아닌 문자를 포함 할 수 있습니까? (0) | 2020.12.04 |
---|---|
리소스 ID의 정수 배열 만들기 (0) | 2020.12.04 |
품질을 100으로 설정하면 JPEG가 손실되지 않습니까? (0) | 2020.12.04 |
3 개의 테이블로 최대 절전 조건 조인 (0) | 2020.12.04 |
서비스 애플리케이션 및 Google Analytics API V3 : 서버 간 OAuth2 인증? (0) | 2020.12.04 |