I writing app for UWP
I have this code
var p = await wc.GetProducts(new Dictionary<string, string>() {
{ "orderby", "id" }, { "filter[search]", "1884" }
});
I try to display data from dictionary like this.
Debug.WriteLine("There");
Debug.WriteLine(p.products);
But it not works.
How I can display data of dictionary ?
Which type does GetProducts() return?
If it's just a Dictionary you can do the following:
foreach(var key in p.Keys)
{
Debug.WriteLine(key);
}
To read a Dictionary --
foreach(KeyValuePair<string,string> kvp in p){
Console.WriteLine(kvp.Key);
Console.WriteLine(kvp.Value);
}
foreach(string key in p.Keys){
Console.WriteLine(key);
Console.WriteLine(p[key]);//value
}
foreach(string value in p.Values){
Console.WriteLine(value);
}
But your problem is that P is a class called products:
Product p = new Product()
{
name = "test product 8",
title = "test product 8",
description = "test product 8",
price = 8.0M
};
You would access the properties of p like:
p.name;
p.title;
p.description;
p.price;
Debug.WriteLine(p.name);
Debug.WriteLine(p.title);//etc
Using the below extension class (Requires Newtonsoft JSON library) you can get a JSON string of any object either with or without readable formatting.
Using the class to get a readable JSON string;
var p = await wc.GetProducts(new Dictionary<string, string>() {
{ "orderby", "id" }, { "filter[search]", "1884" }
});
var jsonString = p.ToFormattedJsonString();
Debug.WriteLine(jsonString);
Using the class to get a plain JSON string without format;
var p = await wc.GetProducts(new Dictionary<string, string>() {
{ "orderby", "id" }, { "filter[search]", "1884" }
});
var jsonString = p.ToJsonString();
Debug.WriteLine(jsonString);
You can also simplfy the above by adding your own extension method such as the below;
public static void ToDebug(this object data)
{
Debug.WriteLine(data.ToFormattedJsonString());
}
The extension class;
using System.Text;
using Newtonsoft.Json;
namespace System
{
public static class JsonExtensions
{
public static string ToFormattedJsonString(this object obj, bool indentWithTab)
{
return indentWithTab
? ToFormattedJsonString(obj, "\t")
: ToFormattedJsonString(obj);
}
public static string ToFormattedJsonString(this object obj, string indentString = " ")
{
return FormatJson(obj.ToJsonString(), indentString);
}
public static string ToJsonString(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
public static T DeserializeJsonString<T>(this string jsonString)
{
return JsonConvert.DeserializeObject<T>(jsonString);
}
private static string FormatJson(string jsonString, string indentString)
{
var indent = 0;
var quoted = false;
var builder = new StringBuilder();
for (var i = 0; i < jsonString.Length; i++)
{
var character = jsonString[i];
switch (character)
{
case '{':
case '[':
builder.Append(character);
if (!quoted)
{
builder.AppendLine();
builder.RepeatAppend(++indent, indentString);
}
break;
case '}':
case ']':
if (!quoted)
{
builder.AppendLine();
builder.RepeatAppend(--indent, indentString);
}
builder.Append(character);
break;
case '"':
builder.Append(character);
bool escaped = false;
var index = i;
while (index > 0 && jsonString[--index] == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
builder.Append(character);
if (!quoted)
{
builder.AppendLine();
builder.RepeatAppend(indent, indentString);
}
break;
case ':':
builder.Append(character);
if (!quoted)
builder.Append(" ");
break;
default:
builder.Append(character);
break;
}
}
return builder.ToString();
}
public static StringBuilder RepeatAppend(this StringBuilder builder, int count, string format,
params object[] parameters)
{
if (count <= 0 || string.IsNullOrEmpty(format))
return builder;
for (int i = 0; i < count; i++)
{
builder.AppendFormat(format, parameters);
}
return builder;
}
}
}
Related
from this Answer I learned how to flatten a JSON object in c#.
from JSON String:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
To:
The following are lines of strings, not an object
menu.id:file
menu.value:File
menu.popup.menuitem[0].value:New
menu.popup.menuitem[0].onclick:CreateNewDoc()
menu.popup.menuitem[1].value:Open
menu.popup.menuitem[1].onclick:OpenDoc()
menu.popup.menuitem[2].value:Close
menu.popup.menuitem[2].onclick:CloseDoc()
Now, i want to reverse the process.
I can found implementations from this question but it is in JavaScript.
How do I unflatten (return structured JSON from lines) it in C# with json.net?
I managed to solve it out.
Below is my code combined with Sarath Rachuri's flattening code.
I did not test it in too many cases, so it could be buggy.
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace JSONHelper
{
class JSONFlattener
{
private enum JSONType{
OBJECT, ARRAY
}
public static Dictionary<string, string> Flatten(JObject jsonObject)
{
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
{
properties.Add(jToken.Path, jToken.ToString());
return properties;
});
return results;
}
public static JObject Unflatten(IDictionary<string, string> keyValues)
{
JContainer result = null;
JsonMergeSettings setting = new JsonMergeSettings();
setting.MergeArrayHandling = MergeArrayHandling.Merge;
foreach (var pathValue in keyValues)
{
if (result == null)
{
result = UnflatenSingle(pathValue);
}
else
{
result.Merge(UnflatenSingle(pathValue), setting);
}
}
return result as JObject;
}
private static JContainer UnflatenSingle(KeyValuePair<string, string> keyValue)
{
string path = keyValue.Key;
string value = keyValue.Value;
var pathSegments = SplitPath(path);
JContainer lastItem = null;
//build from leaf to root
foreach (var pathSegment in pathSegments.Reverse())
{
var type = GetJSONType(pathSegment);
switch (type)
{
case JSONType.OBJECT:
var obj = new JObject();
if (null == lastItem)
{
obj.Add(pathSegment,value);
}
else
{
obj.Add(pathSegment,lastItem);
}
lastItem = obj;
break;
case JSONType.ARRAY:
var array = new JArray();
int index = GetArrayIndex(pathSegment);
array = FillEmpty(array, index);
if (lastItem == null)
{
array[index] = value;
}
else
{
array[index] = lastItem;
}
lastItem = array;
break;
}
}
return lastItem;
}
public static IList<string> SplitPath(string path){
IList<string> result = new List<string>();
Regex reg = new Regex(#"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
foreach (Match match in reg.Matches(path))
{
result.Add(match.Value);
}
return result;
}
private static JArray FillEmpty(JArray array, int index)
{
for (int i = 0; i <= index; i++)
{
array.Add(null);
}
return array;
}
private static JSONType GetJSONType(string pathSegment)
{
int x;
return int.TryParse(pathSegment, out x) ? JSONType.ARRAY : JSONType.OBJECT;
}
private static int GetArrayIndex(string pathSegment)
{
int result;
if (int.TryParse(pathSegment, out result))
{
return result;
}
throw new Exception("Unable to parse array index: " + pathSegment);
}
}
}
Purely System.Text.Json solution for unflatteing JSON. Requires .Net 6.
private static JsonNode Unflatten(Dictionary<string, JsonValue> source)
{
var regex = new System.Text.RegularExpressions.Regex(#"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
JsonNode node = JsonNode.Parse("{}");
foreach (var keyValue in source)
{
var pathSegments = regex.Matches(keyValue.Key).Select(m => m.Value).ToArray();
for (int i = 0; i < pathSegments.Length; i++)
{
var currentSegmentType = GetSegmentKind(pathSegments[i]);
if (currentSegmentType == JsonValueKind.Object)
{
if (node[pathSegments[i]] == null)
{
if (pathSegments[i] == pathSegments[pathSegments.Length - 1])
{
node[pathSegments[i]] = keyValue.Value;
node = node.Root;
}
else
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[pathSegments[i]] = JsonNode.Parse("{}");
}
else
{
node[pathSegments[i]] = JsonNode.Parse("[]");
}
node = node[pathSegments[i]];
}
}
else
{
node = node[pathSegments[i]];
}
}
else
{
if (!int.TryParse(pathSegments[i], out int index))
{
throw new Exception("Cannot parse index");
}
while (node.AsArray().Count - 1 < index)
{
node.AsArray().Add(null);
}
if (i == pathSegments.Length - 1)
{
node[index] = keyValue.Value;
node = node.Root;
}
else
{
if (node[index] == null)
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[index] = JsonNode.Parse("{}");
}
else
{
node[index] = JsonNode.Parse("[]");
}
}
node = node[index];
}
}
}
}
return node;
}
private static JsonValueKind GetSegmentKind(string pathSegment) =>
int.TryParse(pathSegment, out _) ? JsonValueKind.Array : JsonValueKind.Object;
To flatten a JSON object:
arrayJSON.stringify()
The following javascript question is the same problem i'm attempting to solve but in c#
How can I merge 2 dot notation strings to a GraphQL query string
Expected structure is
{
"Case": {
"Owner": {
"Name": null,
"ProfilePic": null
},
"CaseNo": null,
"FieldOfLaw":{
"Name": null
},
"CaseType": {
"Name": null
},
"CaseSubType": {
"Name": null
},
},
"Client":{
"Policy":{
"PolicyNo": null
}
}
}
and my current output is
{
"Case": {
"Owner": {
"Name": null,
"ProfilePic": null
},
"CaseNo": null,
"FieldOfLaw": null,
"CaseType": null,
"CaseSubType": null
}
}
Below is my attempt using ExpandoObjects to try dynamically generate the objects needed. Any advice or pointers in the right direction would be appreciated.
public static void Main(string[] args)
{
var FieldList = new List<string>
{
"Case.Owner.Name",
"Case.Owner.ProfilePic",
"Case.CaseNo",
"Case.FieldOfLaw.Name",
"Case.CaseType.Name",
"Case.CaseSubType.Name",
"Client.Policy.PolicyNo",
};
Parser graphQL = new Parser();
var result = graphQL.Parse(FieldList);
Console.Write(result);
}
Below is the actual parse method, so i'm running an aggregation function over each element to create and return the expando objects into the initial holder. I traverse each split string recursively and exit the recursion once there are no more items left in the split list.
public class Parser
{
public string Parse(List<string> fieldList)
{
// List<ExpandoObject> queryHolder = new List<ExpandoObject>();
ExpandoObject intialSeed = new ExpandoObject();
fieldList.Aggregate(intialSeed, (holder, field) =>
{
holder = ParseToObject(holder, field.Split('.').ToList());
return holder;
});
return JsonConvert.SerializeObject(intialSeed);
}
public ExpandoObject ParseToObject(ExpandoObject holder, List<string> fieldSplit, string previousKey = null)
{
if (fieldSplit.Any())
{
var item = fieldSplit.Shift();
if (item == null)
return holder;
// If the current item doesn't exists in the dictionary
if (!((IDictionary<string, object>)holder).ContainsKey(item))
{
if (((IDictionary<string, object>)holder).Keys.Count() == 0)
holder.TryAdd(item, null);
else
_ = ((IDictionary<string, object>)holder).GetItemByKeyRecursively(previousKey, item);
}
previousKey = item;
ParseToObject(holder, fieldSplit, previousKey);
}
return holder;
}
}
Here are my two extensions methods, i'm having an issue with the GetItemByKeyRecursively when it goes into the 3rd level in it's recursion so example.
I'm adding FieldOfLaw it adds the property to the Case expandoObject but doesn't know how to get back to the leaf containing Owner, CaseNo etc.
public static class CollectionExtensions
{
public static T Shift<T>(this IList<T> list)
{
var shiftedElement = list.FirstOrDefault();
list.RemoveAt(0);
return shiftedElement;
}
public static IDictionary<string, object> GetItemByKeyRecursively(this IDictionary<string, object> dictionary, string parentKey, string keyToCreate)
{
foreach (string key in dictionary.Keys)
{
var leaf = dictionary[key];
if (key == parentKey)
{
var #value = dictionary[key];
if (#value is ExpandoObject)
{
(#value as ExpandoObject).TryAdd(keyToCreate, null);
}
else if (#value == null)
{
var item = new ExpandoObject();
item.TryAdd(keyToCreate, null);
dictionary[key] = item;
}
return dictionary;
}
if (leaf == null)
continue;
return GetItemByKeyRecursively((IDictionary<string, object>)leaf, parentKey, keyToCreate);
}
return null;
}
}
Nothing you can't accomplish mostly declaratively.
public string Parse(List<string> fieldList)
{
var fieldPaths = fieldList.Select(x => x.Split('.').ToList());
var groups = fieldPaths.GroupBy(x => x.First(), x => x.Skip(1));
return ParseGroups(groups, 1);
}
private string ParseGroups(IEnumerable<IGrouping<string, IEnumerable<string>>> groups, int level)
{
string indent = new string('\t', level - 1);
var groupResults = groups.Select(g =>
!g.First().Any() ?
$"\t{indent}{g.Key}: null" :
$"\t{indent}{g.Key}: " + string.Join(", \n",
ParseGroups(g.GroupBy(x => x.First(), x => x.Skip(1)), level + 1))
);
return indent + "{\n" + string.Join(", \n", groupResults) + "\n" + indent + "}";
}
See the complete sample code here: https://dotnetfiddle.net/RLygjt
This is just the sample code. I was wondering if there is a better way of replacing if block
public enum FileType
{
Unknown = 0,
Text = 1,
Word = 2,
Excel = 3,
Csv = 4
}
private static string GetFormatedFile(string fileName)
{
var file = FileType.Unknown;
if (fileName.Contains(".txt"))
{
file = FileType.Text;
}
else if(fileName.Contains(".doc"))
{
file = FileType.Word;
}
else if(fileName.Contains(".xlsx"))
{
file = FileType.Excel;
}
else if (fileName.Contains(".csv"))
{
file = FileType.Csv;
}
switch (file)
{
case FileType.Text: return fileName.Split(".")[3];
case FileType.Word: return fileName.Split("_")[4];
case FileType.Excel: return fileName.Split(".")[3];
case FileType.Csv: return fileName.Split("_")[4];
default: throw new NotSupportedException($"File Type not ready => {file}");
}
}
You can map your extensions in a dictionary and then do a lookup like so:
var map =
new Dictionary<String, FileType>()
{
{".txt", FileType.Text},
{".doc", FileType.Word},
{".xlsx", FileType.Excel},
{".csv", FileType.Csv},
};
var file = FileType.Unknown;
if (map.TryGetValue(Path.GetExtension(fileName), out var type))
{
file = type;
}
interface IFileNameProvider
{
string GetFileName(string name);
bool CanHandle(string name);
}
class WordCsvFileNameProvider : IFileNameProvider
{
public string GetFileName(string name){
return name.Split("_")[4];
}
public bool CanHandle(string name){
return name.Contains(".doc") || name.Contains(".csv");
}
}
class TextExcelFileNameProvider : IFileNameProvider
{
public string GetFileName(string name){
return name.Split(".")[3];
}
public bool CanHandle(string name){
return name.Contains(".txt") || name.Contains(".xlsx");
}
}
You inject the providers and then use them as follows:
private static string GetFormatedFile(string fileName)
{
var provider = fileNameProviders.FirstOrDefault(fnp => fnp.CanHandle(fileName));
if(provider == null)
throw new NotSupportedException($"File Type not ready => {file}");
return provider.GetFileName(fileName);
}
You can just get rid of the enum and switch altogether.
private static string GetFormatedFile(string fileName)
{
var ext = Path.GetExtension(fileName);
switch (ext)
{
case ".txt":
return fileName.Split(new char[] { '.' })[3]; break;
case ".doc":
return fileName.Split(new char[] { '_' })[3]; break;
default:
throw new NotSupportedException("$"File Type not ready => {ext}"");
}
}
I have a template which is stored as a string: "[u1:firstname] [u1:lastname]"
I need to convert the template to an Expression that outputs a string.
I've already written a parser to pull out the tokens but I'm not quite sure how to build an Expression for it. The code has been simplified below.
public class Account {
private Func<Account, string> template;
public User User1 { get; set; }
public User User2 { get; set; }
public string AccountName => this.template(this);
public void SetTemplate(Expression<Func<Account, string>> template) {
this.template = template.Compile();
}
}
public class User {
public string FirstName { get; set; }
public string LastName { get; set; }
}
The name of an account is defined by the template, and if the properties associated with the template change, so does the account name.
If I set the expression manually it works:
var account = new Account().SetTemplate(a => $"{a.User1.FirstName} {a.User2.LastName}");
But how do I build that Expression more dynamically?
Right now I'm doing something like this:
using TemplateExpression = Expression<Func<Account, string>>;
string templateString = "[u1:firstname] [u1:lastname]";
var expressions = new List<Expression>();
for (int i = 0; i < templateString.Length; ++i) {
var token = getToken(templateString, i);
switch (token) {
case "u1:firstname":
TemplateExpression u1FirstNameExpr = a => a.User1.FirstName;
expressions.Add(u1FirstNameExpr);
break;
case "u1:lastname":
TemplateExpression u1LastNameExpr = a => a.User1.LastName;
expressions.Add(u1LastNameExpr);
break;
// other possible tokens.
default: // constant
var constant = Expression.Constant(token);
expressions.Add(constant);
break;
}
}
But I have no idea how to combine those expressions into one expression that resembles the string interpolation above. I thought about maybe combining the expressions using string.Concat instead, but I couldn't get that to work either. Would maybe string.Format work better? But I'm still not sure how to construct that Expression.
Note that I had to rewrite the getToken to be able to test the method. I'm using Expression.Invoke because it is the easiest method to call an Expression from another Expression. In the end nearly all the code is the preparation of a string containing the format like "Hello {0} world {1}" and the array of objects that are passed in the string.Format.
public static string getToken(string templateString, ref int i, out bool isToken)
{
int j = i;
if (templateString[j] == '{')
{
isToken = true;
j++;
int k = templateString.IndexOf('}', j);
if (k == -1)
{
throw new Exception();
}
i = k + 1;
return templateString.Substring(j, k - j);
}
else
{
isToken = false;
i++;
return templateString[j].ToString();
}
}
public static Expression<Func<Account, string>> CreateTemplate(string templateString)
{
var formatObjs = new List<Expression>();
var formatString = new StringBuilder();
int parameterNumber = 0;
var accountParameter = Expression.Parameter(typeof(Account), "a");
for (int i = 0; i < templateString.Length;)
{
bool isToken;
var token = getToken(templateString, ref i, out isToken);
if (isToken)
{
Expression<Func<Account, string>> member;
switch (token)
{
case "u1:firstname":
member = a => a.User1.FirstName;
break;
case "u1:lastname":
member = a => a.User1.LastName;
break;
// other possible tokens.
default: // constant
throw new Exception();
}
formatObjs.Add(Expression.Invoke(member, accountParameter));
formatString.Append('{');
formatString.Append(parameterNumber);
formatString.Append('}');
parameterNumber++;
}
else
{
formatString.Append(token);
}
}
var formatMethod = typeof(string).GetMethod("Format", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(string), typeof(object[]) }, null);
var formatConstantExpression = Expression.Constant(formatString.ToString());
var formatObjsExpression = Expression.NewArrayInit(typeof(object), formatObjs);
var lambdaExpression = Expression.Lambda<Func<Account, string>>(Expression.Call(formatMethod, formatConstantExpression, formatObjsExpression), accountParameter);
return lambdaExpression;
}
Use it like:
var acc = new Account
{
User1 = new User { FirstName = "Foo", LastName = "Bar" }
};
acc.SetTemplate(Account.CreateTemplate("Hello {u1:firstname} World {u1:lastname}!!!"));
string name = acc.AccountName;
Is it possible to get a value based on a switch statement in C#?
public int NumCandyBars (string candyBar)
{
get {
switch (candyBar)
{
case "twix":
{ return _numTwix; }
case "snickers":
return _numSnickers;
case "kitkat":
return _numKitKat;
default: return 0;
}
}
}
so the code above should return a value based on the type of string based to it.
public int NumCandyBars (string candyBar)
{
int retval = 0;
switch (candyBar)
{
case "twix":
retval = _numTwix;
break;
case "snickers":
retval =_numSnickers;
break;
case "kitkat":
retval = _numKitKat;
break;
}
return retval;
}
public enum CandyBar
{
twix,
snickers,
kitkat
}
public int NumCandyBars (CandyBar candyBar)
{
switch (candyBar)
{
case CandyBar.twix:
return _numTwix;
case CandyBar.snickers:
return _numSnickers;
case CandyBar.kitkat:
return _numKitKat;
default: return 0;
}
}
Don't necessarily need polymorphism, but select requires a constant value which Enum satisfies in this context.
In my opinion you are better off avoiding switch in favour of using a dictionary.
Like this:
public int NumCandyBars (string candyBar)
{
var map = new Dictionary<string, int>()
{
{ "twix", _numTwix },
{ "snickers", _numSnickers },
{ "kitkat", _numKitKat },
};
return map.ContainsKey(candyBar) ? map[candyBar] : 0;
}
The advantage here is that map can be moved out as a field and then be programmatically modified at run-time.
private Dictionary<string, int> map = new Dictionary<string, int>()
{
{ "twix", _numTwix },
{ "snickers", _numSnickers },
{ "kitkat", _numKitKat },
};
public int NumCandyBars (string candyBar)
{
return map.ContainsKey(candyBar) ? map[candyBar] : 0;
}
public void SomeMethod()
{
map["mars"] = 42;
}
Or, alternatively you can load the data from a config file, or pull it from an enum or database, and your NumCandyBars need not change.