Dynamic json object with numerical keys - c#

I have a json object which I converted to dynamic C# object with help of this answer. It works just fine, but trouble is that this object has numerical keys. For instance,
var jsStr = "{address:{"100": {...}}}";
So I can't wirte
dynObj.address.100
And, as I know, I can't use indexers to get this object like this
dynObj.address["100"]
Please explain to me how I can get this working.

As far as I can see from the source code he resolves the properties through a private dictionary, so you have to use reflection to access the dictionary key, or modify his code a bit so that TryGetMember in DynamicJSONObject is the following (and use __numeric__ to get the key e.g. data.address.__numeric__100, and then avoid using __numeric__ as a key):
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var name = binder.Name;
//Code to check if key is of form __numeric__<number> so that numeric keys can be accessed
if (binder != null && binder.Name != null && binder.Name.StartsWith("__numeric__"))
{
name = binder.Name.Substring(11);
}
if (!_dictionary.TryGetValue(name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
{
result = new DynamicJsonObject(dictionary);
return true;
}
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
if (arrayList[0] is IDictionary<string, object>)
result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
else
result = new List<object>(arrayList.Cast<object>());
}
return true;
}

My opensource framework ImpromptuInterface has methods to call dynamic members via string name of any C# 4 dynamic object.
object tOut =Impromptu.InvokeGet(dynObj.address,"100");
I tested it with an ExpandoObject it seemed to work just fine.

An identifier must start with a
letter, underscore (_), or dollar sign
($); subsequent characters can also be
digits (0-9). Because JavaScript is
case sensitive, letters include the
characters "A" through "Z" (uppercase)
and the characters "a" through "z"
(lowercase). Starting with JavaScript
1.5, ISO 8859-1 or Unicode letters (or \uXXXX Unicode escape sequences) can
be used in identifiers.
Quoted from: http://en.wikipedia.org/wiki/JavaScript_syntax#Variables
Oh I am sorry mis understood the question, well here you go with a working example you can adjust to your needs:
<script>
var jsStr = {address:{'100': 'test'}};
var test = jsStr.address;
console.log(test);
alert(test[100]);
</script>
btw key CAN be numeric (as you see in the example in the answer), only the identifiers cannot. so you have to access just like you tried. you only have to leave away the quotes for numeric keys! and your json string will not be an object without evaluation, so in this example its strictly speaking a javascript object and not json but it doesnt matter to t he subject

Related

If I'm using non-nullable reference types, how do I show that I didn't find anything?

I've enabled the C# 8.0 non-nullable reference types feature in one of my projects, but now I'm unclear about how to represent missing data.
For example, I'm reading a file whose lines are colon-separated key/value pairs. Sometimes there's more than one colon on a line. In that case, the text before the first colon is the key, and the rest is the value. My code to parse each line looks like this:
public (string key, string value) GetKeyValue(string line)
{
var split = line.Split(':');
if (split.Length == 2)
return (split[0].Trim(), split[1].Trim());
else if (split.Length > 2)
{
var joined = string.Join(":", split.ToList().Skip(1));
return (split[0].Trim(), joined.Trim());
}
else
{
Debug.Print($"Couldn't parse this into key/value: {line}");
return (null, null);
}
}
What this does: If we have just one colon, return the key and value. If we have more than one, join the rest of the text after the first colon, then return the key and value. Otherwise we have no colons and can't parse it, so return a null tuple. (Let's assume this last case can reasonably happen; I can't just throw and call it a bad file.)
Obviously that last line gets a nullability warning unless I change the declaration to
public (string? key, string? value) GetKeyValue(string line)
Now in F# I would just use an Option type and in the no-colon case, I'd return None.
But C# doesn't have an Option type. I could return ("", ""), but to me that doesn't seem better than nulls.
In a case like this, what's a good way to say "I didn't find anything" without using nulls?
You could include if the result was successful in parsing by just returning a flag:
public class Result
{
private Result(){}
public bool Successful {get;private set;} = false;
public string Key {get; private set;} = string.Empty;
public string Value {get; private set;} = string.Empty;
public static Successful(string key, string value)
{
return new Result
{
Successful = true,
Key = key,
Value = value
};
}
public static Failed()
{
return new Result();
}
}
public Result GetKeyValue(string line){
return Result.Failed();
}
Then you could use it like
var result = GetKeyValue("yoda");
if(result.Successful)
{
// do something...
}
Alternatiely you could return 2 diffrent types and use pattern matching 👍
Actually, I realize now that part of the problem is that my method is doing two separate things:
Determine whether the line has a key.
Return the key and value.
Thus the return value has to indicate both whether there's a key and value, and what the key and value are.
I can simplify by doing the first item separately:
bool HasKey(string line)
{
var split = line.Split(':');
return split.Length >= 2;
}
Then in the method I posted, if there's no key, I can throw and say that the lines need to be filtered by HasKey first.
Putting on my functional thinking cap, an idiomatic return type would be IEnumerable<(string?,string?)>. The only change to your code would be to change return to yield return, and to remove the return statement if nothing is found.
public IEnumerable<(string? key, string? value)> GetKeyValue(string line)
{
var split = line.Split(':');
if (split.Length == 2)
return (split[0].Trim(), split[1].Trim());
else if (split.Length > 2)
{
var joined = string.Join(":", split.ToList().Skip(1));
yield return (split[0].Trim(), joined.Trim());
}
else
{
Debug.Print($"Couldn't parse this into key/value: {line}");
}
}
The caller then has several options on how to handle the response.
If they want to check if the key was found the old-fashioned eway, do this:
var result = GetKeyValue(line).SingleOrDefault();
if (!result.HasValue) HandleKeyNotFound();
If they prefer to throw an exception if the key is not found, they'd do this:
var result = GetKeyValue(line).Single();
If they just want to be quiet about it they can use ForEach, which will use the key and value if they are found and simply do nothing if they are not:
foreach (var result in GetKeyValue(line)) DoSomething(result.Item1, result.Item2);
Also, for what it's worth, I'd suggest using KeyValuePair instead of a tuple, since it clearly communicates the purpose of the fields.

How can I get a single string out of the Dictionary? C#

I've some problems using the Dictionary. How can I get the value back from the Dictionary
I have stored some data in the Dictionary
Dictionary<UserSettings.Languages, string> objectiveLanguages
= new Dictionary<UserSettings.Languages, string>();
objectiveLanguages.Add(UserSettings.Languages.English, objectiveNameEnglish);
objectiveLanguages.Add(UserSettings.Languages.German, objectiveNameGerman);
Could someone explain me, how to retreive the stored value again?
Two options:
You know that the value will be present.
string str = objectiveLanguages[UserSettings.Languages.English];
return str;
You don't know that the value will be present, or you'd like to throw your own exception.
string str;
if(objectiveLanguages.TryGet(UserSettings.Languages.English, out str))
return str;
else
throw new ArgumentException(); // or return null, or whatever.
The choice between these depends on circumstances. Since you're dealing with an enum (or equivalent), the first should probably suffice. If the user was, say, entering data, on the other hand, I'd probably go with the second. It's a similar decision to that of using int.Parse versus int.TryParse.
Console.WriteLine("For key UserSettings.Languages.English, value = {0}.",
objectiveLanguages[UserSettings.Languages.English]);
/* returns the value of this key, if present.
*/
Ref the MSDN documentation here for more info.
Dictionary<UserSettings.Languages, string> objectiveLanguages
= new Dictionary<UserSettings.Languages, string>();
objectiveLanguages.Add(UserSettings.Languages.English, objectiveNameEnglish);
objectiveLanguages.Add(UserSettings.Languages.German, objectiveNameGerman);
// get some value out of the dict
string dictContent = objectiveLanguages[UserSettings.Languages.English];
Use square brackets with a key inside. For example:
// returns objectiveNameEnglish
var retrievedValue = objectiveLanguages[UserSettings.Languages.English];
For every pair you add, the first item is the key and the second item is the value mapped to it. MSDN has good examples https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx

Using an array in String.Format

Currently I'm working on an web-API dependent application. I have made a class with a lot of API strings like: /api/lol/static-data/{region}/v1/champion/{id}. Also I made a method:
public static String request(String type, Object[] param)
{
return "";
}
This is going to do the requesting stuff. Because it's very different with each request type how many parameters are being used, I'm using an array for this. Now the question is, is it posssible to String.Format using an array for the paramaters while the keys in the strings are not numbers? Or does anyone know how to do this in a different way?
No, string.Format only supports index-based parameter specifications.
This:
"/api/lol/static-data/{region}/v1/champion/{id}"
^^^^^^^^ ^^^^
will have to be handled using a different method, like string.Replace or a Regex.
You will need to:
Decide on the appropriate method for doing the replacements
Decide how an array with index-based values should map to the parameters, are they positional? ie. {region} is the first element of the array, {id} is the second, etc.?
Here is a simple LINQPad program that demonstrates how I would do it (though I would add a bit more error handling, maybe caching of the reflection info if this is executed a lot, some unit-tests, etc.):
void Main()
{
string input = "/api/lol/static-data/{region}/v1/champion/{id}";
string output = ReplaceArguments(input, new
{
region = "Europe",
id = 42
});
output.Dump();
}
public static string ReplaceArguments(string input, object arguments)
{
if (arguments == null || input == null)
return input;
var argumentsType = arguments.GetType();
var re = new Regex(#"\{(?<name>[^}]+)\}");
return re.Replace(input, match =>
{
var pi = argumentsType.GetProperty(match.Groups["name"].Value);
if (pi == null)
return match.Value;
return (pi.GetValue(arguments) ?? string.Empty).ToString();
});
}

String to enum conversion in C#

I have a combo box where I am displaying some entries like:
Equals
Not Equals
Less Than
Greater Than
Notice that these strings contain spaces. I have a enum defined which matches to these entries like:
enum Operation{Equals, Not_Equals, Less_Than, Greater_Than};
Since space is not allowed, I have used _ character.
Now, is there any way to convert given string automatically to an enum element without writing a loop or a set of if conditions my self in C#?
I suggest building a Dictionary<string, Operation> to map friendly names to enum constants and use normal naming conventions in the elements themselves.
enum Operation{ Equals, NotEquals, LessThan, GreaterThan };
var dict = new Dictionary<string, Operation> {
{ "Equals", Operation.Equals },
{ "Not Equals", Operation.NotEquals },
{ "Less Than", Operation.LessThan },
{ "Greater Than", Operation.GreaterThan }
};
var op = dict[str];
Alternatively, if you want to stick to your current method, you can do (which I recommend against doing):
var op = (Operation)Enum.Parse(typeof(Operation), str.Replace(' ', '_'));
Operation enumVal = (Operation)Enum.Parse(typeof(Operation), "Equals")
For "Not Equals", you obv need to replace spaces with underscores in the above statement
EDIT: The following version replaces the spaces with underscores before attempting the parsing:
string someInputText;
var operation = (Operation)Enum.Parse(typeof(Operation), someInputText.Replace(" ", "_"));
Either create a dedicated mapper using a dictionary (per Mehrdad's answer) or implement a TypeConverter.
Your custom TypeConverter could either replace " " -> "_" (and vice versa) or it could reflect the enumeration and use an attribute for determining the display text of the item.
enum Operation
{
[DisplayName("Equals")]
Equals,
[DisplayName("Not Equals")]
Not_Equals,
[DisplayName("Less Than")]
Less_Than,
[DisplayName("Greater Than")]
Greater_Than
};
public class OperationTypeConverter : TypeConverter
{
private static Dictionary<string, Operation> operationMap;
static OperationTypeConverter()
{
BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.GetField
| BindingFlags.Public;
operationMap = enumType.GetFields(bindingFlags).ToDictionary(
c => GetDisplayName(c)
);
}
private static string GetDisplayName(FieldInfo field, Type enumType)
{
DisplayNameAttribute attr = (DisplayNameAttribute)Attribute.GetCustomAttribute(typeof(DisplayNameAttribute));
return (attr != null) ? attr.DisplayName : field.Name;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
string stringValue = value as string;
if (stringValue != null)
{
Operation operation;
if (operationMap.TryGetValue(stringValue, out operation))
{
return operation;
}
else
{
throw new ArgumentException("Cannot convert '" + stringValue + "' to Operation");
}
}
}
}
This implementation could be improved in several ways:
Make it generic
Implement ConvertTo
Support FlagsAttribute
You can use the Parse method:
Operarion operation = (Operation)Enum.Parse(typeof(Operation), "Not_Equals");
Some examples here
Why use another way : convert Enumeration to String?
Just generate the items of your combo box from your Enumeration.
in C#, you can add extension methods to enum types. See
http://msdn.microsoft.com/en-us/library/bb383974.aspx
You could use this approach to add toString(Operation op), fromString(String str) and toLocalizedString(Operation op) methods to your enum types. The method that you use to lookup the particular string depends on your application and should be consistent with what you do in similar cases. Using a dictionary as others have suggested seems like a good first approach as long as you don't need full localization in your app.
I would use a singleton of this enum mapper class that performs much faster than Enum.Parse (which uses reflection and is really slow).
You can then use EnumFromString(typeof(YourEnum), "stringValue") to get your enum.
As of C# 8 you can do that using switches. In your example I believe the code would be like this.
enum Operation{Equals, Not_Equals, Less_Than, Greater_Than};
public static string OperationString(Operation opString) =>
opString switch
{
Operation.Equals => "Equals",
Operation.Not_Equals => "Not Equals",
Operation.Less_Than=> "Less Than",
Operation.Greater_Than=> "Greater Than",
_ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(opString )),
};
See here for the documentation.

Expanding a message template

I have a set of templates for emails that my app sends out. The templates have codes embedded in them that correspond to properties of my business object.
Is there a more elegant way than calling
string.Replace("{!MyProperty!}", item.MyProperty.ToString())
a zillion times? Maybe XMLTransform, regular expressions, or some other magic? I'm using C# 3.5 .
First of all, when I do this I use StringBuilder.Replace() as I have found that its performance is much better suited when working with 3 or more replacements.
Of course there are other ways of doing it, but I've found that it is not usually worth the extra effort to try other items.
You might be able to use Reflection I guess to automate the replacement, that might be the only "better" way.
public static string Translate(string pattern, object context)
{
return Regex.Replace(pattern, #"\{!(\w+)!}", match => {
string tag = match.Groups[1].Value;
if (context != null)
{
PropertyInfo prop = context.GetType().GetProperty(tag);
if (prop != null)
{
object value = prop.GetValue(context);
if (value != null)
{
return value.ToString();
}
}
}
return "";
});
}
Translate("Hello {!User!}. Welcome to {!GroupName!}!", new {
User = "John",
GroupName = "The Community"
}); // -> "Hello John. Welcome to The Community!"
https://ideone.com/D9J31c
There's a built in WebControl, System.Web.UI.WebControls.MailDefinition that does string replacements (among other things). Pity they tightly coupled it to the Smtp settings in app.config and a web control, and then made it sealed to foil inheritors.
But, it does handle a few things you'd most likely want in a mail template engine - body text from a file, html email, embedded objects, etc. Reflector shows the actual replacement is handled with a foreach loop and Regex.Replace - which seems a reasonable choice to me as well.
A quick glance through shows that if you can live with the from address being in the app.config (you can change it on the returned MailMessage afterwards), you only need the owner control for embedded resources or the BodyFileName.
If you're using ASP.NET or can live with the limitations - I'd choose MailDefinition. Otherwise, just do a foreach over a dictionary and a Regex.Replace. It's a little memory hungry, because of the repeated allocations of body - but they're short lived and shouldn't pose much of a problem.
var replacements = new Dictionary<string, object>() {
{ "Property1", obj.Property1 },
{ "Property2", obj.Property2 },
{ "Property3", obj.Property3 },
{ "Property4", obj.Property4 },
}
foreach (KeyValuePair<string, object> kvp in replacement) {
body = Regex.Replace(body, kvp.Key, kvp.Value.ToString());
}
If you really have a lot of properties, then read your body first with Regex.Match and reflect to the properties instead.
You could do it using a regex, but your regex replace differs also for each property. I would stick with the string.Replace.
Use reflection to retrieve the properties and replace it in a loop:
foreach (string property in properties)
{
string.Replace("{!"+property+"!}",ReflectionHelper.GetStringValue(item,property));
}
Just implement your ReflectionHelper.GetStringValue method and use reflection to retrieve all the properties on your item object type.
After looking at the examples previously included, I thought I'd look at the real code.
#mark-brackett You were closer than you knew.
//The guts of MailDefinition.CreateMailMessage
//from https://github.com/Microsoft/referencesource/blob/master/System.Web/UI/WebControls/MailDefinition.cs
if (replacements != null && !String.IsNullOrEmpty(body)) {
foreach (object key in replacements.Keys) {
string fromString = key as string;
string toString = replacements[key] as string;
if ((fromString == null) || (toString == null)) {
throw new ArgumentException(SR.GetString(SR.MailDefinition_InvalidReplacements));
}
// DevDiv 151177
// According to http://msdn2.microsoft.com/en-us/library/ewy2t5e0.aspx, some special
// constructs (starting with "$") are recognized in the replacement patterns. References of
// these constructs will be replaced with predefined strings in the final output. To use the
// character "$" as is in the replacement patterns, we need to replace all references of single "$"
// with "$$", because "$$" in replacement patterns are replaced with a single "$" in the
// final output.
toString = toString.Replace("$", "$$");
body = Regex.Replace(body, fromString, toString, RegexOptions.IgnoreCase);
}
}

Categories

Resources