Let's say I have the following string:
string postData = "state={2}&country={3}&geolocation={0}&city={1}";
And I have another list of strings. Its size could be in this case 4 at most.
I'm trying to create a method which replaces the numbers in my postData variable, depending on the list size. Something like the following method:
private string UnknownSizeStringFormat(string postData, params string[] stringsToReplace)
{
return string.format(postData, stringsToReplace);
}
The above method works as long as the list's size is 4. The thing is, on the first call, my list's size could be smaller than 4, so if it's 0 for example, I would like to replace each number inside the brackets with an empty string. My return value should be:
"state=&country=&geolocation=&city="
If its size is one, and the first member in the list is "21,27" my return string should be:
"state=&country=&geolocation=21,27&city="
And so on...
I could use a loop or a Regular Expression for this purpose but I've been wondering if there is a better way, a Linq solution perhaps? What I do know is how many numbers postData could have at most, which in my case is 4.
Again, I could do that with a loop or a Regular Expression, but I'm trying to make it as short as possible
Edit: The postData string could vary. That was just an example. Its size or content could be different
What I do know is how many numbers postData could have at most
Then how about this:
static string UnknownSizeStringFormat(string format, int maxArgs, params object[] args)
{
Array.Resize(ref args, maxArgs);
return string.Format(format, args);
}
so you can use:
string postData = "state={2}&country={3}&geolocation={0}&city={1}";
var result = UnknownSizeStringFormat(postData, 4);
You could use something like this:
private string UnknownSizeStringFormat(string postData, params string[] stringsToReplace)
{
string[] temp = { "", "", "", "" };
Array.ConstrainedCopy(stringsToReplace, 0, temp, 0, stringsToReplace.Length);
return string.format(postData, temp);
}
If you are allowed to write in C#6, then I would suggest you the following quick solution
Define a class that has as properties the parameters you refer to and ovveride the ToString method in a such a way to return the url you want.
public class CustomUrl
{
public string State {get;set;}
public string Country { get;set;}
public string Geolocation {get;set;}
public string City {get;set;}
public ovveride string ToString() =>
$"state={State}&country={Country}&geolocation={Geolocation}&city={City}";
}
you can use it as:
var customUrl = new CustomUrl
{
Geolocation = "21,27";
}
Then calling customUrl.ToString(), you would get:
"state=&country=&geolocation=21,27&city="
While creating another customer url as:
var customUrl = new CustomUrl();
and the calling customUrl.ToString() you would get:
"state=&country=&geolocation=&city="
If you are not allowed to write in C#, you have to modify a bit the class's definition, like below:
public class CustomUrl
{
public string State {get;set;}
public string Country { get;set;}
public string Geolocation {get;set;}
public string City {get;set;}
public ovveride string ToString()
{
retrun string.Format("state={0}&country={1}&geolocation={2}&city={3}",State,Country,Geolocation,City);
}
}
However, the best solution can be found at Named Formats Redux, the Henri formatter. If you implement this, you could call it as an extension method, like below:
var template = "state={state}&country={country}&geolocation={geo}&city={city}";
var customUrl = template.HenriFormat(new { geo = "21,72"});
The reason I say that this is the best solution is the fact that you would implement it once and you could use it anywhere, without have to implement a custom class for a case like the above.
I would start by adjusting your postData based on stringsToReplace.Length. You can use a switch/case method to control this. This is non-tested code, so please use with debug to verify.
string postData = "state={2}&country={3}&geolocation={0}&city={1}";
private string UnknownSizeStringFormat(string postData, params string[] stringsToReplace)
{
switch(stringsToReplace.Length){
case 0:
postData = "state={0}&country={0}&geolocation={0}&city={0}";
break;
case 1:
postData = "state={0}&country={0}&geolocation={1}&city={0}";
break;
case 2:
postData = "state={2}&country={0}&geolocation={1}&city={0}";
break;
case 3:
postData = "state={2}&country={3}&geolocation={1}&city={0}";
break;
case 4:
postData = "state={2}&country={3}&geolocation={1}&city={4}";
break;
return string.format(postData, String.Empty, stringsToReplace);
}
}
Related
I have this code:
public class TaskDetails
{
private Dictionary<string, string> _arguments;
public string Arguments
{
get
{
string _tmp = "";
foreach (var _item in _arguments)
{
string _str = _item.Key + ": " + _item.Value + "<br>";
_tmp += String.Join(_str, Environment.NewLine);
}
return _tmp;
}
set { _arguments = value; }
}
}
The idea is very simple: I put value into property in the form I'm comfortable with up the code (i.e. Dictionary), but I get it from property in the form for representation down the code - the string.
But IDE says about set accessor that I cannot implicitly convert type string to Dictionary. Silly IDE, I want to put in Dictionary into Dictionary-type private property.
How, then, should I do it to publicly set property as Dictionary and publicly get this property as string?
The notion of representing a dictionary as a <br> delimited set of plaintext strings is a little unusual and will probably need tweaks at some point in the future (e.g. if you wanted to put the text into div elements to make them valid HTML5, or if you wanted to provide escaping to prevent XSS, or you might want to use CRLF instead of Environment.Newline to match the HTTP protocol). So I'd suggest you encapsulate that idea in a class of its own. This will improve your maintainability.
Then you can give you custom class some implicit conversion operators, and you can write code that automatically converts to/from string or dictionary, allowing you to code it the way you were thinking (even though it's doing something slightly different under the covers).
public class HtmlDictionary //Or some other name
{
private readonly Dictionary<string, string> _arguments;
public HtmlDictionary(Dictionary<string, string> arguments)
{
_arguments = arguments;
}
public override string ToString()
{
string tmp = "";
foreach (var item in arguments)
{
string str = $"{item.Key}: {item.Value}<br>\r\n";
tmp += str;
}
return tmp;
}
public static implicit operator string(HtmlDictionary input) => input.ToString();
public static implicit operator HtmlDictionary(Dictionary<string, string> input) => new HtmlDictionary(input);
}
Now you can do this:
public class TaskDetails
{
public HtmlDictionary Arguments { get; set }
}
var o = new TaskDetails();
o.Arguments = new Dictionary<string,string>();
string s = o.Arguments;
It looks like you are setting a different type than you are getting, but under the covers the compiler is just implicitly converting for you.
P.S. The underscore generally precedes member variables only. It will confuse a lot of people if you put an underscore in front of local variable names too.
I want to pass C# object as query string & i used following code to get the desired result.
class Program
{
public static 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());
}
static void Main(string[] args)
{
Filters fil = new Filters();
fil.Age = 10;
fil.Id = "some id";
fil.Divisions = new List<string>();
fil.Divisions.Add("div 1");
fil.Divisions.Add("div 2");
fil.Divisions.Add("div 3");
fil.Names = new List<string>();
fil.Names.Add("name 1");
fil.Names.Add("name 2");
fil.Names.Add("name 3");
var queryStr = GetQueryString(fil);
Console.ReadKey();
}
}
public class Filters
{
public List<string> Names { get; set; }
public List<string> Divisions { get; set; }
public int Age { get; set; }
public string Id { get; set; }
}
using the above code give me following result:
Names=System.Collections.Generic.List%601%5bSystem.String%5d&Divisions=System.Collections.Generic.List%601%5bSystem.String%5d&Age=10&Id=some+id
The output is not a valid query string. I need help to convert any POCO class into query string format.
I have a similar JavaScript object and i am able to convert it into correct query string.
{
"id":"some id",
"age":10,
"division":["div 1","div 2","div 3"],
"names":["name 1","name 2","name 3"]
}
using Jquery I can say $.param(obj) and this will result in:
"id=some+id&age=10&division%5B%5D=div+1&division%5B%5D=div+2&division%5B%5D=div+3&names%5B%5D=name+1&names%5B%5D=name+2&names%5B%5D=name+3"
I want a similar output using c#.
It looks like The problem is that you are calling ToString() on your objects. List<String>.ToString() will return "List<System.String>", which is what you're seeing, except URL encoded.
You will need to either:
Provide an iterface with a ToQueryString method:
public interface IQueryStringable
{
string ToQueryString();
}
and have all classes you might want to use as query strings implement it, or
Rewrite your reflection so that it iterates sequences. Something like (pseudocode):
Get property.
See if it is an instance of IEnumerable. If not, proceed as before
Otherwise:
for each item, construct a string consisting of the property name, "[]=" and the value of that item.
Concatenate the produced strings and urlencode it.
For sanity's sake, I would recommend option 1, and I enjoy playing with reflection. It gets more complex if you want to allow arbitrary nesting of classes.
I want to use facebook's API and i find it hard to convert objects to urlEncoded.
so, for now i have something like:
string postData = JsonConvert.SerializeObject(req);
postData = postData.Replace(#"\", "");
postData = HttpUtility.UrlEncode(postData);
byte[] data = Encoding.UTF8.GetBytes(postData);
string facebookUrl = "https://graph.facebook.com/v2.5/";
problem is that facebook doesn't accept jsons but UrlEncoded data, as it seems, correct me if im wrong.
So, Im pretty sure converting objects to UrlEncoded string is impossbile in .Net 4.5.1 because I've tried to use some of the answers for this questions that are while ago they are not working for me.
for example:
var result = new List<string>();
foreach (var property in TypeDescriptor.GetProperties(req))
{
result.Add(property.Name + "=" + property.GetValue(req));
}
postData = string.Join("&", result);
but .Name and .GetValue aren't defined at all.
Would like to get some help with that, TIA.
Objects i use:
internal sealed class FacebookValidationRequest
{
public string access_token;
public fbReq[] batch;
public string method;
public string format;
public int pretty;
public int suppress_http_code;
public string debug;
public FacebookValidationRequest(string appId, string userToken)
{
access_token = userToken;
batch = new[]
{
//test code
new fbReq("GET", "me"),
new fbReq("GET", "me/friends?limit=50") //,
//new fbReq("GET", "app?access_token=" + userToken)
};
method = "post";
format = "json";
pretty = 0;
suppress_http_code = 1;
debug = "all";
}
}
internal sealed class fbReq
{
public string method;
public string relative_url;
public fbReq(string m, string url)
{
method = m;
relative_url = url;
}
}
FacebookValidationRequest req = new FacebookValidationRequest(appToken, userToken);
Also, took the token for the facebook debugger site
how facebook wants to object to look like after encoding:
access_token=mytoken&batch=%5B%7B%22method%22%3A%22GET%22%2C%20%22relative_url%22%3A%22me%22%7D%2C%7B%22method%22%3A%22GET%22%2C%20%22relative_url%22%3A%22me%2Ffriends%3Flimit%3D50%22%7D%5D&debug=all&fields=id%2Cname&format=json&method=post&pretty=0&suppress_http_code=1
Seems to me that the easiest way to do this is with Attributes to describe your properties, just like how the .Net Json's DataContract system does it. Basically, you assign an attribute to each property you want serialized, and make that attribute contain the name to serialize it as. I don't think you want to get into the mess of actually writing your own DataContractSerializer, though, so it might be easier to simply create your own Property class and a simple serializer using reflection.
The attribute class:
[AttributeUsage(AttributeTargets.Property)]
public sealed class UrlEncodeAttribute : System.Attribute
{
public String Name { get; private set; }
public UrlEncodeAttribute(String name)
{
this.Name = name;
}
}
Then, to apply to your data class... put the attributes on all properties:
internal sealed class FacebookValidationRequest
{
[UrlEncodeAttribute("access_token")]
public String AccessToken { get; set; }
[UrlEncodeAttribute("method")]
public String Method { get; set; }
[UrlEncodeAttribute("format")]
public String Format { get; set; }
[UrlEncodeAttribute("pretty")]
public Int32 Pretty { get; set; }
[UrlEncodeAttribute("suppress_http_code")]
public Int32 SuppressHttpCode { get; set; }
[UrlEncodeAttribute("debug")]
public string Debug { get; set; }
public fbReq[] Batch { get; set; }
[UrlEncodeAttribute("batch")]
public String BatchString
{
get
{
// put your json serialization code here to return
// the contents of Batch as json string.
}
}
}
As you see, Batch does not have the UrlEncodeAttribute, while its string representation BatchString does. Its get is what will be called by the serializer, so you can put the conversion code in there.
Also note that thanks to the text names you give in the attributes, your properties don't need to have the names you actually get in the serialization, which looks much cleaner in my opinion. C#'s own serialization to xml and json works in the same way.
Now, let's take a look at the actual serialization, using reflection to get those properties:
public static String Serialize(Object obj, Boolean includeEmpty)
{
// go over the properties, see which ones have a UrlEncodeAttribute, and process them.
StringBuilder sb = new StringBuilder();
PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
object[] attrs = p.GetCustomAttributes(true);
foreach (Object attr in attrs)
{
UrlEncodeAttribute fldAttr = attr as UrlEncodeAttribute;
if (attr == null)
continue;
String objectName = fldAttr.Name;
Object objectDataObj = p.GetValue(obj, null);
String objectData = objectDataObj == null ? String.Empty : objectDataObj.ToString();
if (objectData.Length > 0 || includeEmpty)
{
objectData = HttpUtility.UrlEncode(objectData);
objectName= HttpUtility.UrlEncode(objectName);
if (sb.Length > 0)
sb.Append("&");
sb.Append(objectName).Append("=").Append(objectData);
}
break; // Only handle one UrlEncodeAttribute per property.
}
}
return sb.ToString();
}
A more advanced version of this could be made by including a serialization method property in the UrlEncodeAttribute class (probably best done with an enum), so you can simply specify to serialize the array on the fly using json. You'll obviously need to put the actual json converter into the Serialize function then. I thought using the getter on a dummy property as preparation method was simpler, here.
Obviously, calling it is simply this: (assuming here the Serialize() function is in a class called UrlEncodeSerializer)
FacebookValidationRequest fbreq = new FacebookValidationRequest();
// fill your data into fbreq here
// ...
// includeEmpty is set to true for testing here, but normally in
// UrlEncoded any missing property is just seen as empty anyway, so
// there should be no real difference.
String serialized = UrlEncodeSerializer.Serialize(fbreq, true);
Is it possible to store some kind of custom binary data in a string object?
For example, I have the string "Hello!", which represents a sequence of bytes.
Can I store an additional byte on that string that will be meaningful only to me?
For example, an extra byte that represents the string's length.
Is there a way to tell the .NET's decoder (which decodes these bytes) to ignore these custom bytes?
You really shouldn't go about it that way. How about creating a class that does what you want:
public class myString
{
public string stringValue;
public byte someSpecialByte;
public override string ToString()
{
return stringValue;
}
}
Then you can store whatever information you want in the class.
myString s = new myString();
s.stringValue = "Hello!"
s.someSpecialByte = 0;
Edit (In response to the constructor comment):
Add this to the class:
public myString(){} //default constructor
public myString(string sValue, byte specialByte)
{
stringValue = sValue;
someSpecialByte = specialByte;
}
I've got some static data that I'm experimenting with in C# and the first method looks like this which is basically declaring them.
public class PrivilegeProfiles
{
string PROFILE_ID;
string COMPANY_CODE;
string PRIVILEGE_CODE;
public PrivilegeProfiles(string PROFILE_ID, string COMPANY_CODE, string PRIVILEGE_CODE)
{
this.PROFILE_ID = PROFILE_ID;
this.COMPANY_CODE = COMPANY_CODE;
this.PRIVILEGE_CODE = PRIVILEGE_CODE;
}
}
that's all fine and good but I've got a second method with a .Add keyword and since it only takes 3 arguments I can't add all the static data I need. PRIVILEGE_CODE has multiple bits of data where as PROFILE_ID and COMPANY_CODE only have one. Are there certain brackets I've gotta use or is there a way I've gotta format it for it to work?
public ServiceResponse GetPrivileges()
{
ServiceResponse sR = new ServiceResponse();
List<PrivilegeProfiles> privilegeProfiles;
privilegeProfiles.Add(new PrivilegeProfiles("Train Manager","GW",["DASuper" "DAAccess" "MRSuper", "MRAccess"]);
sR.DataResponse=privilegeProfiles;
return sR;
}
I think you might want the PRIVILEGE_CODE field to be an array of strings instead of a string. For example:
public class PrivilegeProfiles
{
string PROFILE_ID;
string COMPANY_CODE;
string[] PRIVILEGE_CODE;
public PrivilegeProfiles(string aPROFILE_ID, string aCOMPANY_CODE, string[] aPRIVILEGE_CODE)
{
this.PROFILE_ID = aPROFILE_ID;
this.COMPANY_CODE = aCOMPANY_CODE;
this.PRIVILEGE_CODE = aPRIVILEGE_CODE;
}
}
and
public ServiceResponse GetPrivileges()
{
ServiceResponse sR = new ServiceResponse();
List<PrivilegeProfiles> privilegeProfiles;
privilegeProfiles.Add(new PrivilegeProfiles("Train Manager","GW", new string[] {"DASuper","DAAccess","MRSuper","MRAccess"});
sR.DataResponse=privilegeProfiles;
return sR;
}
You either add more variables to your PrivilegeProfiles class that can hold all the information you have or you find a format so that all your PRIVILEGE_CODE data fits into a string. Some examples for your ["DASuper" "DAAccess" "MRSuper", "MRAccess"] as a string could be:
"DASuper,DAAccess,MRSuper,MRAccess"
"DASuper;DAAccess;MRSuper;MRAccess"
"DASuper DAAccess MRSuper MRAccess"
whatever you please