Reputation: 355
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;
}
}
Upvotes: 1
Views: 618
Reputation: 434
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
Upvotes: 1