Convert a custom object to a query string
I wrote an extension method that takes a custom object and converts its properties to a query string for use in a URL. You can specify what properties you want included by using a [QueryString]
attribute. For example:
class Thing
{
[QueryString]
public int Id {get; set;}
[QueryString]
public string Name {get; set;}
public object MetaData {get; set;}
}
var thing = new Thing { Id = 1, Name = "Test", MetaData = someObj };
thing.ToQueryString(); // outputs `?Id=1&Name=Test
Here's the code for the extension method:
public static string ToQueryString(this object obj)
{
StringBuilder queryStringBuilder = new StringBuilder("?");
Type objType = obj.GetType();
// Get properties marked with `[QueryString]`
List<PropertyInfo> props = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)))
.ToList();
foreach (var prop in props)
{
string name = prop.Name;
var value = prop.GetValue(obj, null);
if (value != null)
{
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
}
}
return queryStringBuilder.ToString();
}
The code works just fine, but it seems a bit messy to me. Specifically, checking if we are on the last property seems a bit messy, because we end up doing the same thing, just nixing one character. Is there a way to make that a little cleaner? Are there any other way's this method could be simplified?
c# strings reflection
add a comment |
I wrote an extension method that takes a custom object and converts its properties to a query string for use in a URL. You can specify what properties you want included by using a [QueryString]
attribute. For example:
class Thing
{
[QueryString]
public int Id {get; set;}
[QueryString]
public string Name {get; set;}
public object MetaData {get; set;}
}
var thing = new Thing { Id = 1, Name = "Test", MetaData = someObj };
thing.ToQueryString(); // outputs `?Id=1&Name=Test
Here's the code for the extension method:
public static string ToQueryString(this object obj)
{
StringBuilder queryStringBuilder = new StringBuilder("?");
Type objType = obj.GetType();
// Get properties marked with `[QueryString]`
List<PropertyInfo> props = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)))
.ToList();
foreach (var prop in props)
{
string name = prop.Name;
var value = prop.GetValue(obj, null);
if (value != null)
{
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
}
}
return queryStringBuilder.ToString();
}
The code works just fine, but it seems a bit messy to me. Specifically, checking if we are on the last property seems a bit messy, because we end up doing the same thing, just nixing one character. Is there a way to make that a little cleaner? Are there any other way's this method could be simplified?
c# strings reflection
Also to force caller to use an attribute is problematic (IMO) because she can't use anonymous types (which might be very handy).
– Adriano Repetti
Mar 8 '18 at 15:55
@AdrianoRepetti the reason I did that, is because I figured, you may have certain properties you don't want added in the string.
– an earwig
Mar 8 '18 at 15:57
2
Of course it depends on the use case(s) but I expect query strings to be small objects (also because I do not want to pollute model with extraneous attributes!) then I'd go the other way (but that's just my opinion, obviously)
– Adriano Repetti
Mar 8 '18 at 15:59
1
I agree with @AdrianoRepetti, adding attributes is in this case unnecessary. You might also want to create different query strings from the same object. I suggest taking a glimpse at something that I wrote a couple of days ago that can easly be adjusted to produce query strings instead.
– t3chb0t
Mar 8 '18 at 16:10
add a comment |
I wrote an extension method that takes a custom object and converts its properties to a query string for use in a URL. You can specify what properties you want included by using a [QueryString]
attribute. For example:
class Thing
{
[QueryString]
public int Id {get; set;}
[QueryString]
public string Name {get; set;}
public object MetaData {get; set;}
}
var thing = new Thing { Id = 1, Name = "Test", MetaData = someObj };
thing.ToQueryString(); // outputs `?Id=1&Name=Test
Here's the code for the extension method:
public static string ToQueryString(this object obj)
{
StringBuilder queryStringBuilder = new StringBuilder("?");
Type objType = obj.GetType();
// Get properties marked with `[QueryString]`
List<PropertyInfo> props = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)))
.ToList();
foreach (var prop in props)
{
string name = prop.Name;
var value = prop.GetValue(obj, null);
if (value != null)
{
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
}
}
return queryStringBuilder.ToString();
}
The code works just fine, but it seems a bit messy to me. Specifically, checking if we are on the last property seems a bit messy, because we end up doing the same thing, just nixing one character. Is there a way to make that a little cleaner? Are there any other way's this method could be simplified?
c# strings reflection
I wrote an extension method that takes a custom object and converts its properties to a query string for use in a URL. You can specify what properties you want included by using a [QueryString]
attribute. For example:
class Thing
{
[QueryString]
public int Id {get; set;}
[QueryString]
public string Name {get; set;}
public object MetaData {get; set;}
}
var thing = new Thing { Id = 1, Name = "Test", MetaData = someObj };
thing.ToQueryString(); // outputs `?Id=1&Name=Test
Here's the code for the extension method:
public static string ToQueryString(this object obj)
{
StringBuilder queryStringBuilder = new StringBuilder("?");
Type objType = obj.GetType();
// Get properties marked with `[QueryString]`
List<PropertyInfo> props = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)))
.ToList();
foreach (var prop in props)
{
string name = prop.Name;
var value = prop.GetValue(obj, null);
if (value != null)
{
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
}
}
return queryStringBuilder.ToString();
}
The code works just fine, but it seems a bit messy to me. Specifically, checking if we are on the last property seems a bit messy, because we end up doing the same thing, just nixing one character. Is there a way to make that a little cleaner? Are there any other way's this method could be simplified?
c# strings reflection
c# strings reflection
asked Mar 8 '18 at 15:36
an earwig
2501312
2501312
Also to force caller to use an attribute is problematic (IMO) because she can't use anonymous types (which might be very handy).
– Adriano Repetti
Mar 8 '18 at 15:55
@AdrianoRepetti the reason I did that, is because I figured, you may have certain properties you don't want added in the string.
– an earwig
Mar 8 '18 at 15:57
2
Of course it depends on the use case(s) but I expect query strings to be small objects (also because I do not want to pollute model with extraneous attributes!) then I'd go the other way (but that's just my opinion, obviously)
– Adriano Repetti
Mar 8 '18 at 15:59
1
I agree with @AdrianoRepetti, adding attributes is in this case unnecessary. You might also want to create different query strings from the same object. I suggest taking a glimpse at something that I wrote a couple of days ago that can easly be adjusted to produce query strings instead.
– t3chb0t
Mar 8 '18 at 16:10
add a comment |
Also to force caller to use an attribute is problematic (IMO) because she can't use anonymous types (which might be very handy).
– Adriano Repetti
Mar 8 '18 at 15:55
@AdrianoRepetti the reason I did that, is because I figured, you may have certain properties you don't want added in the string.
– an earwig
Mar 8 '18 at 15:57
2
Of course it depends on the use case(s) but I expect query strings to be small objects (also because I do not want to pollute model with extraneous attributes!) then I'd go the other way (but that's just my opinion, obviously)
– Adriano Repetti
Mar 8 '18 at 15:59
1
I agree with @AdrianoRepetti, adding attributes is in this case unnecessary. You might also want to create different query strings from the same object. I suggest taking a glimpse at something that I wrote a couple of days ago that can easly be adjusted to produce query strings instead.
– t3chb0t
Mar 8 '18 at 16:10
Also to force caller to use an attribute is problematic (IMO) because she can't use anonymous types (which might be very handy).
– Adriano Repetti
Mar 8 '18 at 15:55
Also to force caller to use an attribute is problematic (IMO) because she can't use anonymous types (which might be very handy).
– Adriano Repetti
Mar 8 '18 at 15:55
@AdrianoRepetti the reason I did that, is because I figured, you may have certain properties you don't want added in the string.
– an earwig
Mar 8 '18 at 15:57
@AdrianoRepetti the reason I did that, is because I figured, you may have certain properties you don't want added in the string.
– an earwig
Mar 8 '18 at 15:57
2
2
Of course it depends on the use case(s) but I expect query strings to be small objects (also because I do not want to pollute model with extraneous attributes!) then I'd go the other way (but that's just my opinion, obviously)
– Adriano Repetti
Mar 8 '18 at 15:59
Of course it depends on the use case(s) but I expect query strings to be small objects (also because I do not want to pollute model with extraneous attributes!) then I'd go the other way (but that's just my opinion, obviously)
– Adriano Repetti
Mar 8 '18 at 15:59
1
1
I agree with @AdrianoRepetti, adding attributes is in this case unnecessary. You might also want to create different query strings from the same object. I suggest taking a glimpse at something that I wrote a couple of days ago that can easly be adjusted to produce query strings instead.
– t3chb0t
Mar 8 '18 at 16:10
I agree with @AdrianoRepetti, adding attributes is in this case unnecessary. You might also want to create different query strings from the same object. I suggest taking a glimpse at something that I wrote a couple of days ago that can easly be adjusted to produce query strings instead.
– t3chb0t
Mar 8 '18 at 16:10
add a comment |
5 Answers
5
active
oldest
votes
I wouldn't go the StringBuilder
route, simply because you need to handle the "what if I'm at the start/end" problem. Instead, simply collect the "URL_translated" key-value pairs into an IEnumerable<string>
and then apply string.join()
.
If you're working in .NET Core, Microsoft does all the lifting for you. Here's how they do it.
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
add a comment |
In places where the type is obvious (or irrelevant) then feel free to use var
instead of the type name. Also, consider not naming your variable after the type; this is also generally not necessary.
I also don't think that you need to make props
a list.
A trailing ampersand in the querystring is perfectly legal, so no need to special case it. You can handle it with String.Join
though (see my last version below).
Lastly, since you're already using Linq I figure you can go all of the way. You can merge some of your checks into the Where
check as well to simplify this.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.ForEach(p => qs.Append($"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}&"));
return qs.ToString();
}
I ended up keeping the ToList
here so I could use the ForEach
, but removing that and just looping is fine too.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
var properties = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null);
foreach (var prop of properties)
{
var name = prop .Name;
var value = prop .GetValue(obj);
qa.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value)}&"));
}
return qs.ToString();
}
Ultimately, I think the cleanest method is to use String.Join
and pass the enumerable as the argument. It gets a little long, but there are plenty of ways to change the styling if you don't like that.
public static string ToQueryString(this object obj)
{
return string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
add a comment |
A few ideas
Consider reversing the attribute logic so that you opt OUT of including rather than opt in. If you do it that way, your function will also work with anonymous types and you'll be able to create URLs on the fly very easily. You could even use an existing framework attribute such as IgnoreDataMemberAttribute, which could work well if you plan to use this function on models.
Consider breaking the function up into two different functions. In one function, convert the properties into a dictionary. In the second function, convert a dictionary into a query string. This will give you much more flexibility. With your current setup, you are unable to get the querystring values without going to the trouble of creating the string too.
static Dictionary<string,object> ToPropertyDictionary(this object o)
{
return o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where( p => !Attribute.IsDefined(p, typeof(IgnoreDataMemberAttribute))
.ToDictionary
(
p => p.Name,
p => p.GetValue(o)
);
}
If you do as I suggest in #2 and break it into two stages, you can easily generate the URL (without the weird logic to add the
&
) by usingString.Join
over the dictionary, e.g.
static string ToQueryString(this object o)
{
return string.Join
(
"&",
o.ToPropertyDictionary()
.Where( e => e.Value != null)
.Select
(
e => string.Format
(
"{0}={1}",
e.Key,
UrlEncode(e.Value)
)
)
)
}
Do not include the ? in the results. The caller should be responsible for that. If you do it this way, you can use the results of your function to append to an existing URL that may already have querystring parameters.
You probably do not need to URL-escape the property name, since c# property names can't contain &, space, + or =.
add a comment |
Several people have suggested turning the loop in to a linq query and using string.join, I wholeheartedly endorse them, but if you’re not ready for that there is a minor change that addresses your concern.
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={UriEscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
Can easily be rewritten as:
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
// Check's if this is the last property, if not add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append(“&”);
}
But really, if you are going to do this in an explicit loop, it’s a great place to use a ternary assignment:
var queryDelimeter = props.IndexOf(prop) != (props.Count - 1) ? “&” : “”;
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}{queryDelimeter}");
add a comment |
I've found trying to use attributes on a complex model to designate what you want to turn into a query string begins to break down the more that model is used elsewhere. Because not everywhere it is translated to query strings will you ALWAYS want to same set of query strings. So generally you create a model-per-querystring-context. These models make attributes moot as the models are only going to have what they need anyways.
var MyObject = new {
FirstName = "John",
LastName = "Doe",
MySSN = "111-11-1111",
MyCC = "4820895069259441",
MyCVV = "4820895069259441",
MyDOB = DateTime.Now.AddYears(-20),
MyImageArray = new byte{1,2,3,4,5,6,7,8,9}
};
var MyQueryGraph = new {
MyObject.FirstName,
MyObject.LastName,
DOB = MyObject.MyDOB.ToString("MM/dd/yyyy"),
Image = Convert.ToBase64String(MyObject.MyImageArray)
};
As far as turning an object to query string, I've used:
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
static class Helper
{
public static string ToQueryString(this object model)
{
var serialized = JsonConvert.SerializeObject(model);
var deserialized = JsonConvert.DeserializeObject<Dictionary<string,string>>(serialized);
var result = deserialized.Select((kvp) => kvp.Key.ToString() + "=" + Uri.EscapeDataString(kvp.Value)).Aggregate((p1,p2) => p1 + "&" + p2);
return result;
}
}
and then:
MyQueryGraph.ToQueryString();
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
|
show 2 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f189137%2fconvert-a-custom-object-to-a-query-string%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
I wouldn't go the StringBuilder
route, simply because you need to handle the "what if I'm at the start/end" problem. Instead, simply collect the "URL_translated" key-value pairs into an IEnumerable<string>
and then apply string.join()
.
If you're working in .NET Core, Microsoft does all the lifting for you. Here's how they do it.
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
add a comment |
I wouldn't go the StringBuilder
route, simply because you need to handle the "what if I'm at the start/end" problem. Instead, simply collect the "URL_translated" key-value pairs into an IEnumerable<string>
and then apply string.join()
.
If you're working in .NET Core, Microsoft does all the lifting for you. Here's how they do it.
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
add a comment |
I wouldn't go the StringBuilder
route, simply because you need to handle the "what if I'm at the start/end" problem. Instead, simply collect the "URL_translated" key-value pairs into an IEnumerable<string>
and then apply string.join()
.
If you're working in .NET Core, Microsoft does all the lifting for you. Here's how they do it.
I wouldn't go the StringBuilder
route, simply because you need to handle the "what if I'm at the start/end" problem. Instead, simply collect the "URL_translated" key-value pairs into an IEnumerable<string>
and then apply string.join()
.
If you're working in .NET Core, Microsoft does all the lifting for you. Here's how they do it.
answered Mar 8 '18 at 15:49
BCdotWEB
8,63411638
8,63411638
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
add a comment |
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
Ah cool, unfortunately not on .NET Core, but it's nice to know it has such a utility
– an earwig
Mar 8 '18 at 15:52
add a comment |
In places where the type is obvious (or irrelevant) then feel free to use var
instead of the type name. Also, consider not naming your variable after the type; this is also generally not necessary.
I also don't think that you need to make props
a list.
A trailing ampersand in the querystring is perfectly legal, so no need to special case it. You can handle it with String.Join
though (see my last version below).
Lastly, since you're already using Linq I figure you can go all of the way. You can merge some of your checks into the Where
check as well to simplify this.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.ForEach(p => qs.Append($"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}&"));
return qs.ToString();
}
I ended up keeping the ToList
here so I could use the ForEach
, but removing that and just looping is fine too.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
var properties = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null);
foreach (var prop of properties)
{
var name = prop .Name;
var value = prop .GetValue(obj);
qa.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value)}&"));
}
return qs.ToString();
}
Ultimately, I think the cleanest method is to use String.Join
and pass the enumerable as the argument. It gets a little long, but there are plenty of ways to change the styling if you don't like that.
public static string ToQueryString(this object obj)
{
return string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
add a comment |
In places where the type is obvious (or irrelevant) then feel free to use var
instead of the type name. Also, consider not naming your variable after the type; this is also generally not necessary.
I also don't think that you need to make props
a list.
A trailing ampersand in the querystring is perfectly legal, so no need to special case it. You can handle it with String.Join
though (see my last version below).
Lastly, since you're already using Linq I figure you can go all of the way. You can merge some of your checks into the Where
check as well to simplify this.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.ForEach(p => qs.Append($"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}&"));
return qs.ToString();
}
I ended up keeping the ToList
here so I could use the ForEach
, but removing that and just looping is fine too.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
var properties = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null);
foreach (var prop of properties)
{
var name = prop .Name;
var value = prop .GetValue(obj);
qa.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value)}&"));
}
return qs.ToString();
}
Ultimately, I think the cleanest method is to use String.Join
and pass the enumerable as the argument. It gets a little long, but there are plenty of ways to change the styling if you don't like that.
public static string ToQueryString(this object obj)
{
return string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
add a comment |
In places where the type is obvious (or irrelevant) then feel free to use var
instead of the type name. Also, consider not naming your variable after the type; this is also generally not necessary.
I also don't think that you need to make props
a list.
A trailing ampersand in the querystring is perfectly legal, so no need to special case it. You can handle it with String.Join
though (see my last version below).
Lastly, since you're already using Linq I figure you can go all of the way. You can merge some of your checks into the Where
check as well to simplify this.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.ForEach(p => qs.Append($"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}&"));
return qs.ToString();
}
I ended up keeping the ToList
here so I could use the ForEach
, but removing that and just looping is fine too.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
var properties = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null);
foreach (var prop of properties)
{
var name = prop .Name;
var value = prop .GetValue(obj);
qa.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value)}&"));
}
return qs.ToString();
}
Ultimately, I think the cleanest method is to use String.Join
and pass the enumerable as the argument. It gets a little long, but there are plenty of ways to change the styling if you don't like that.
public static string ToQueryString(this object obj)
{
return string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
In places where the type is obvious (or irrelevant) then feel free to use var
instead of the type name. Also, consider not naming your variable after the type; this is also generally not necessary.
I also don't think that you need to make props
a list.
A trailing ampersand in the querystring is perfectly legal, so no need to special case it. You can handle it with String.Join
though (see my last version below).
Lastly, since you're already using Linq I figure you can go all of the way. You can merge some of your checks into the Where
check as well to simplify this.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.ForEach(p => qs.Append($"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}&"));
return qs.ToString();
}
I ended up keeping the ToList
here so I could use the ForEach
, but removing that and just looping is fine too.
public static string ToQueryString(this object obj)
{
var qs = new StringBuilder("?");
var objType = obj.GetType();
var properties = objType.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null);
foreach (var prop of properties)
{
var name = prop .Name;
var value = prop .GetValue(obj);
qa.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value)}&"));
}
return qs.ToString();
}
Ultimately, I think the cleanest method is to use String.Join
and pass the enumerable as the argument. It gets a little long, but there are plenty of ways to change the styling if you don't like that.
public static string ToQueryString(this object obj)
{
return string.Join("&", obj.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(QueryStringAttribute)) && p.GetValue(obj, null) != null)
.Select(p => $"{Uri.EscapeDataString(p.Name)}={Uri.EscapeDataString(p.GetValue(obj).ToString())}"));
}
edited Mar 8 '18 at 16:47
answered Mar 8 '18 at 15:56
Dannnno
5,4821749
5,4821749
add a comment |
add a comment |
A few ideas
Consider reversing the attribute logic so that you opt OUT of including rather than opt in. If you do it that way, your function will also work with anonymous types and you'll be able to create URLs on the fly very easily. You could even use an existing framework attribute such as IgnoreDataMemberAttribute, which could work well if you plan to use this function on models.
Consider breaking the function up into two different functions. In one function, convert the properties into a dictionary. In the second function, convert a dictionary into a query string. This will give you much more flexibility. With your current setup, you are unable to get the querystring values without going to the trouble of creating the string too.
static Dictionary<string,object> ToPropertyDictionary(this object o)
{
return o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where( p => !Attribute.IsDefined(p, typeof(IgnoreDataMemberAttribute))
.ToDictionary
(
p => p.Name,
p => p.GetValue(o)
);
}
If you do as I suggest in #2 and break it into two stages, you can easily generate the URL (without the weird logic to add the
&
) by usingString.Join
over the dictionary, e.g.
static string ToQueryString(this object o)
{
return string.Join
(
"&",
o.ToPropertyDictionary()
.Where( e => e.Value != null)
.Select
(
e => string.Format
(
"{0}={1}",
e.Key,
UrlEncode(e.Value)
)
)
)
}
Do not include the ? in the results. The caller should be responsible for that. If you do it this way, you can use the results of your function to append to an existing URL that may already have querystring parameters.
You probably do not need to URL-escape the property name, since c# property names can't contain &, space, + or =.
add a comment |
A few ideas
Consider reversing the attribute logic so that you opt OUT of including rather than opt in. If you do it that way, your function will also work with anonymous types and you'll be able to create URLs on the fly very easily. You could even use an existing framework attribute such as IgnoreDataMemberAttribute, which could work well if you plan to use this function on models.
Consider breaking the function up into two different functions. In one function, convert the properties into a dictionary. In the second function, convert a dictionary into a query string. This will give you much more flexibility. With your current setup, you are unable to get the querystring values without going to the trouble of creating the string too.
static Dictionary<string,object> ToPropertyDictionary(this object o)
{
return o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where( p => !Attribute.IsDefined(p, typeof(IgnoreDataMemberAttribute))
.ToDictionary
(
p => p.Name,
p => p.GetValue(o)
);
}
If you do as I suggest in #2 and break it into two stages, you can easily generate the URL (without the weird logic to add the
&
) by usingString.Join
over the dictionary, e.g.
static string ToQueryString(this object o)
{
return string.Join
(
"&",
o.ToPropertyDictionary()
.Where( e => e.Value != null)
.Select
(
e => string.Format
(
"{0}={1}",
e.Key,
UrlEncode(e.Value)
)
)
)
}
Do not include the ? in the results. The caller should be responsible for that. If you do it this way, you can use the results of your function to append to an existing URL that may already have querystring parameters.
You probably do not need to URL-escape the property name, since c# property names can't contain &, space, + or =.
add a comment |
A few ideas
Consider reversing the attribute logic so that you opt OUT of including rather than opt in. If you do it that way, your function will also work with anonymous types and you'll be able to create URLs on the fly very easily. You could even use an existing framework attribute such as IgnoreDataMemberAttribute, which could work well if you plan to use this function on models.
Consider breaking the function up into two different functions. In one function, convert the properties into a dictionary. In the second function, convert a dictionary into a query string. This will give you much more flexibility. With your current setup, you are unable to get the querystring values without going to the trouble of creating the string too.
static Dictionary<string,object> ToPropertyDictionary(this object o)
{
return o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where( p => !Attribute.IsDefined(p, typeof(IgnoreDataMemberAttribute))
.ToDictionary
(
p => p.Name,
p => p.GetValue(o)
);
}
If you do as I suggest in #2 and break it into two stages, you can easily generate the URL (without the weird logic to add the
&
) by usingString.Join
over the dictionary, e.g.
static string ToQueryString(this object o)
{
return string.Join
(
"&",
o.ToPropertyDictionary()
.Where( e => e.Value != null)
.Select
(
e => string.Format
(
"{0}={1}",
e.Key,
UrlEncode(e.Value)
)
)
)
}
Do not include the ? in the results. The caller should be responsible for that. If you do it this way, you can use the results of your function to append to an existing URL that may already have querystring parameters.
You probably do not need to URL-escape the property name, since c# property names can't contain &, space, + or =.
A few ideas
Consider reversing the attribute logic so that you opt OUT of including rather than opt in. If you do it that way, your function will also work with anonymous types and you'll be able to create URLs on the fly very easily. You could even use an existing framework attribute such as IgnoreDataMemberAttribute, which could work well if you plan to use this function on models.
Consider breaking the function up into two different functions. In one function, convert the properties into a dictionary. In the second function, convert a dictionary into a query string. This will give you much more flexibility. With your current setup, you are unable to get the querystring values without going to the trouble of creating the string too.
static Dictionary<string,object> ToPropertyDictionary(this object o)
{
return o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where( p => !Attribute.IsDefined(p, typeof(IgnoreDataMemberAttribute))
.ToDictionary
(
p => p.Name,
p => p.GetValue(o)
);
}
If you do as I suggest in #2 and break it into two stages, you can easily generate the URL (without the weird logic to add the
&
) by usingString.Join
over the dictionary, e.g.
static string ToQueryString(this object o)
{
return string.Join
(
"&",
o.ToPropertyDictionary()
.Where( e => e.Value != null)
.Select
(
e => string.Format
(
"{0}={1}",
e.Key,
UrlEncode(e.Value)
)
)
)
}
Do not include the ? in the results. The caller should be responsible for that. If you do it this way, you can use the results of your function to append to an existing URL that may already have querystring parameters.
You probably do not need to URL-escape the property name, since c# property names can't contain &, space, + or =.
edited Mar 8 '18 at 20:11
answered Mar 8 '18 at 19:59
John Wu
54136
54136
add a comment |
add a comment |
Several people have suggested turning the loop in to a linq query and using string.join, I wholeheartedly endorse them, but if you’re not ready for that there is a minor change that addresses your concern.
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={UriEscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
Can easily be rewritten as:
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
// Check's if this is the last property, if not add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append(“&”);
}
But really, if you are going to do this in an explicit loop, it’s a great place to use a ternary assignment:
var queryDelimeter = props.IndexOf(prop) != (props.Count - 1) ? “&” : “”;
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}{queryDelimeter}");
add a comment |
Several people have suggested turning the loop in to a linq query and using string.join, I wholeheartedly endorse them, but if you’re not ready for that there is a minor change that addresses your concern.
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={UriEscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
Can easily be rewritten as:
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
// Check's if this is the last property, if not add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append(“&”);
}
But really, if you are going to do this in an explicit loop, it’s a great place to use a ternary assignment:
var queryDelimeter = props.IndexOf(prop) != (props.Count - 1) ? “&” : “”;
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}{queryDelimeter}");
add a comment |
Several people have suggested turning the loop in to a linq query and using string.join, I wholeheartedly endorse them, but if you’re not ready for that there is a minor change that addresses your concern.
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={UriEscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
Can easily be rewritten as:
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
// Check's if this is the last property, if not add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append(“&”);
}
But really, if you are going to do this in an explicit loop, it’s a great place to use a ternary assignment:
var queryDelimeter = props.IndexOf(prop) != (props.Count - 1) ? “&” : “”;
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}{queryDelimeter}");
Several people have suggested turning the loop in to a linq query and using string.join, I wholeheartedly endorse them, but if you’re not ready for that there is a minor change that addresses your concern.
// Check's if this is the last property, if so, don't add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={UriEscapeDataString(value.ToString())}&");
}
else
{
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
}
Can easily be rewritten as:
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}");
// Check's if this is the last property, if not add an '&'
if (props.IndexOf(prop) != (props.Count - 1))
{
queryStringBuilder.Append(“&”);
}
But really, if you are going to do this in an explicit loop, it’s a great place to use a ternary assignment:
var queryDelimeter = props.IndexOf(prop) != (props.Count - 1) ? “&” : “”;
queryStringBuilder.Append($"{Uri.EscapeDataString(name)}={Uri.EscapeDataString(value.ToString())}{queryDelimeter}");
answered Dec 30 '18 at 3:26
jmoreno
1,173612
1,173612
add a comment |
add a comment |
I've found trying to use attributes on a complex model to designate what you want to turn into a query string begins to break down the more that model is used elsewhere. Because not everywhere it is translated to query strings will you ALWAYS want to same set of query strings. So generally you create a model-per-querystring-context. These models make attributes moot as the models are only going to have what they need anyways.
var MyObject = new {
FirstName = "John",
LastName = "Doe",
MySSN = "111-11-1111",
MyCC = "4820895069259441",
MyCVV = "4820895069259441",
MyDOB = DateTime.Now.AddYears(-20),
MyImageArray = new byte{1,2,3,4,5,6,7,8,9}
};
var MyQueryGraph = new {
MyObject.FirstName,
MyObject.LastName,
DOB = MyObject.MyDOB.ToString("MM/dd/yyyy"),
Image = Convert.ToBase64String(MyObject.MyImageArray)
};
As far as turning an object to query string, I've used:
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
static class Helper
{
public static string ToQueryString(this object model)
{
var serialized = JsonConvert.SerializeObject(model);
var deserialized = JsonConvert.DeserializeObject<Dictionary<string,string>>(serialized);
var result = deserialized.Select((kvp) => kvp.Key.ToString() + "=" + Uri.EscapeDataString(kvp.Value)).Aggregate((p1,p2) => p1 + "&" + p2);
return result;
}
}
and then:
MyQueryGraph.ToQueryString();
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
|
show 2 more comments
I've found trying to use attributes on a complex model to designate what you want to turn into a query string begins to break down the more that model is used elsewhere. Because not everywhere it is translated to query strings will you ALWAYS want to same set of query strings. So generally you create a model-per-querystring-context. These models make attributes moot as the models are only going to have what they need anyways.
var MyObject = new {
FirstName = "John",
LastName = "Doe",
MySSN = "111-11-1111",
MyCC = "4820895069259441",
MyCVV = "4820895069259441",
MyDOB = DateTime.Now.AddYears(-20),
MyImageArray = new byte{1,2,3,4,5,6,7,8,9}
};
var MyQueryGraph = new {
MyObject.FirstName,
MyObject.LastName,
DOB = MyObject.MyDOB.ToString("MM/dd/yyyy"),
Image = Convert.ToBase64String(MyObject.MyImageArray)
};
As far as turning an object to query string, I've used:
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
static class Helper
{
public static string ToQueryString(this object model)
{
var serialized = JsonConvert.SerializeObject(model);
var deserialized = JsonConvert.DeserializeObject<Dictionary<string,string>>(serialized);
var result = deserialized.Select((kvp) => kvp.Key.ToString() + "=" + Uri.EscapeDataString(kvp.Value)).Aggregate((p1,p2) => p1 + "&" + p2);
return result;
}
}
and then:
MyQueryGraph.ToQueryString();
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
|
show 2 more comments
I've found trying to use attributes on a complex model to designate what you want to turn into a query string begins to break down the more that model is used elsewhere. Because not everywhere it is translated to query strings will you ALWAYS want to same set of query strings. So generally you create a model-per-querystring-context. These models make attributes moot as the models are only going to have what they need anyways.
var MyObject = new {
FirstName = "John",
LastName = "Doe",
MySSN = "111-11-1111",
MyCC = "4820895069259441",
MyCVV = "4820895069259441",
MyDOB = DateTime.Now.AddYears(-20),
MyImageArray = new byte{1,2,3,4,5,6,7,8,9}
};
var MyQueryGraph = new {
MyObject.FirstName,
MyObject.LastName,
DOB = MyObject.MyDOB.ToString("MM/dd/yyyy"),
Image = Convert.ToBase64String(MyObject.MyImageArray)
};
As far as turning an object to query string, I've used:
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
static class Helper
{
public static string ToQueryString(this object model)
{
var serialized = JsonConvert.SerializeObject(model);
var deserialized = JsonConvert.DeserializeObject<Dictionary<string,string>>(serialized);
var result = deserialized.Select((kvp) => kvp.Key.ToString() + "=" + Uri.EscapeDataString(kvp.Value)).Aggregate((p1,p2) => p1 + "&" + p2);
return result;
}
}
and then:
MyQueryGraph.ToQueryString();
I've found trying to use attributes on a complex model to designate what you want to turn into a query string begins to break down the more that model is used elsewhere. Because not everywhere it is translated to query strings will you ALWAYS want to same set of query strings. So generally you create a model-per-querystring-context. These models make attributes moot as the models are only going to have what they need anyways.
var MyObject = new {
FirstName = "John",
LastName = "Doe",
MySSN = "111-11-1111",
MyCC = "4820895069259441",
MyCVV = "4820895069259441",
MyDOB = DateTime.Now.AddYears(-20),
MyImageArray = new byte{1,2,3,4,5,6,7,8,9}
};
var MyQueryGraph = new {
MyObject.FirstName,
MyObject.LastName,
DOB = MyObject.MyDOB.ToString("MM/dd/yyyy"),
Image = Convert.ToBase64String(MyObject.MyImageArray)
};
As far as turning an object to query string, I've used:
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
static class Helper
{
public static string ToQueryString(this object model)
{
var serialized = JsonConvert.SerializeObject(model);
var deserialized = JsonConvert.DeserializeObject<Dictionary<string,string>>(serialized);
var result = deserialized.Select((kvp) => kvp.Key.ToString() + "=" + Uri.EscapeDataString(kvp.Value)).Aggregate((p1,p2) => p1 + "&" + p2);
return result;
}
}
and then:
MyQueryGraph.ToQueryString();
answered Mar 8 '18 at 20:00
Rex Henderson
211
211
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
|
show 2 more comments
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
If you're going to down vote, have the courage to say something about it.
– Rex Henderson
Mar 9 '18 at 1:09
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
It's not my DV but I can imagine it's because of the As far as turning an object to query string, I've used and no further explanation at all. At least this is what I would DV it for if it wasn't already DV.
– t3chb0t
Mar 9 '18 at 7:27
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
Bringing in a dependency on Newtonsoft JSON (as common as it may be) and then using a serialize/deserialize for converting to a dictionary instead of using reflection is red flag to me. Whether you like the attribute method or not, using reflection like in @Dannnno's last example is far better in my opinion.
– Matthew FitzGerald-Chamberlain
Mar 9 '18 at 14:53
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I appreciate the responses. I can learn from everybody's perspective and opinion as well :) Well, if you do not already have Newsoft.Json or similar serialization library in your web project, then I would say you are overlooking a powerful capability that makes web communications much more simple. I'm just leveraging what you should already have included and allowing its strength to perform for me what your reflection and enumerations are doing.
– Rex Henderson
Mar 9 '18 at 19:18
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
I'll quantify that any decently sized web project is missing out if it does not have serialization in play. And Newtonsoft uses similar reflection-based approaches and is more thoroughly tested and widely used than custom code
– Rex Henderson
Mar 9 '18 at 19:28
|
show 2 more comments
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f189137%2fconvert-a-custom-object-to-a-query-string%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Also to force caller to use an attribute is problematic (IMO) because she can't use anonymous types (which might be very handy).
– Adriano Repetti
Mar 8 '18 at 15:55
@AdrianoRepetti the reason I did that, is because I figured, you may have certain properties you don't want added in the string.
– an earwig
Mar 8 '18 at 15:57
2
Of course it depends on the use case(s) but I expect query strings to be small objects (also because I do not want to pollute model with extraneous attributes!) then I'd go the other way (but that's just my opinion, obviously)
– Adriano Repetti
Mar 8 '18 at 15:59
1
I agree with @AdrianoRepetti, adding attributes is in this case unnecessary. You might also want to create different query strings from the same object. I suggest taking a glimpse at something that I wrote a couple of days ago that can easly be adjusted to produce query strings instead.
– t3chb0t
Mar 8 '18 at 16:10