Newtonsoft.Json Newtonsoft.Json Newtonsoft.Json.Bson Newtonsoft.Json.Converters Newtonsoft.Json.Linq Newtonsoft.Json.Schema Newtonsoft.Json.Serialization void Main() { string Rendered = JsonFromTemplate(Globals.strTemplate, Globals.strData); Rendered.Dump(); } // render adaptive card JSON public string JsonFromTemplate(string strTemplate, string strData) { string output = ""; // replace instances of $data with x-data // this is to work around JSON Path limitations strTemplate = strTemplate.Replace("$","x-"); // first create JSON objects out of the input JObject template = JObject.Parse(strTemplate); JObject data = JObject.Parse(strData); // this is the old way - regex against the string // maybe do it a better way string queryString = @"..[?(@ =~ /\{(.+)?\}/)]"; List nodeList = template.SelectTokens(queryString).ToList(); // for each templated entity // retrieve entity from data JSON // if empty, remove entity foreach (JValue node in nodeList) { // get string literal for template entity string fulltext = node.Value(); // find the matching element in the data JSON // strip { and } from fulltext string cleantext = fulltext.Substring(1, fulltext.Length - 2); string searchString = "$." + cleantext; // find the token in Data JSON // can be JValue or JArray JToken searchResult = data.SelectToken(searchString); // if this element exists in the data // and isn't $data // replace element with text from Data JSON // otherwise remove it if (searchResult != null) { if (searchResult.GetType() == typeof(JValue)) { string oldValue = node.Value(); string newValue = oldValue.Replace(fulltext, ((JValue)searchResult.Value()).ToString()); node.Value = node.Value().ToString().Replace(oldValue, newValue); } else if (searchResult.GetType() == typeof(JArray)) { // it's a JArray JArray searchArray = (JArray)searchResult; foreach (string item in searchArray) { JObject newItem = new JObject((JObject)node.Parent.Parent); newItem["x-data"].Parent.Remove(); newItem["text"] = item; node.Parent.Parent.Parent.Add(newItem); } node.Parent.Parent.Remove(); } } else if (fulltext.Contains("{x-")) { // this is handled differently } else { // remove this node node.Parent.Parent.Remove(); } } // now find any empty arrays and nuke their parents // keep doing this until there are no more [] left in the JSON while (template.Descendants().OfType().Where(v => v.Type == JTokenType.Array && v.Count() == 0).ToList().Count() > 0) { List EmptyNodes = template.Descendants().OfType().Where(v => v.Type == JTokenType.Array && v.Count() == 0).ToList(); foreach (JArray Node in EmptyNodes) { Node.Parent.Parent.Remove(); } } JsonConvert.SerializeObject(template, Newtonsoft.Json.Formatting.Indented).Dump(); return output; } // set up global statics public class Globals { public static string strData = @" { ""template"" : { ""image"" : ""image"", ""clade"" : ""Clades"", ""name"" : ""Common Name"", ""order"" : ""Order"", ""family"" : ""Family"", ""genus"" : ""Genus"", ""species"" : ""Species (Latin Name)"" }, ""item"" : { ""image"" : ""almonds.jpg"", ""clade"" : [ ""Tracheophytes"", ""Angiosperms"", ""Eudicots"", ""Rosids"" ], ""name"" : ""American Hazelnut"", ""order"" : ""Fagales"", ""family"" : ""Betulaceae"", ""genus"" : ""Corylus"", ""species"" : ""Corylus Americana"" } } "; public static string strTemplate = @" { ""type"": ""AdaptiveCard"", ""body"": [ { ""type"": ""Container"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{item.name}"", ""size"": ""Large"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Center"", ""color"": ""Accent"" }, { ""type"": ""Image"", ""url"": ""{item.image}"", ""altText"": """" }, { ""type"": ""ColumnSet"", ""columns"": [ { ""type"": ""Column"", ""width"": ""stretch"", ""horizontalAlignment"": ""Right"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{template.other}"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Right"" } ] }, { ""type"": ""Column"", ""width"": ""stretch"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{item.other}"" } ] } ] }, { ""type"": ""ColumnSet"", ""columns"": [ { ""type"": ""Column"", ""width"": ""stretch"", ""horizontalAlignment"": ""Right"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{template.name}"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Right"" } ] }, { ""type"": ""Column"", ""width"": ""stretch"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{item.name}"" } ] } ] }, { ""type"": ""ColumnSet"", ""columns"": [ { ""type"": ""Column"", ""width"": ""stretch"", ""horizontalAlignment"": ""Right"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{template.order}"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Right"" } ] }, { ""type"": ""Column"", ""width"": ""stretch"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{item.order}"" } ] } ] }, { ""type"": ""ColumnSet"", ""columns"": [ { ""type"": ""Column"", ""width"": ""stretch"", ""horizontalAlignment"": ""Right"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{template.family}"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Right"" } ] }, { ""type"": ""Column"", ""width"": ""stretch"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{item.genus}"" } ] } ] }, { ""type"": ""ColumnSet"", ""columns"": [ { ""type"": ""Column"", ""width"": ""stretch"", ""horizontalAlignment"": ""Right"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{template.species}"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Right"" } ] }, { ""type"": ""Column"", ""width"": ""stretch"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{item.species}"" } ] } ] }, { ""type"": ""ColumnSet"", ""columns"": [ { ""type"": ""Column"", ""width"": ""stretch"", ""horizontalAlignment"": ""Right"", ""items"": [ { ""type"": ""TextBlock"", ""text"": ""{template.clade}"", ""weight"": ""Bolder"", ""horizontalAlignment"": ""Right"" } ] }, { ""type"": ""Column"", ""width"": ""stretch"", ""items"": [ { ""type"": ""Container"", ""items"": [ { ""$data"": ""{item.clade}"", ""type"": ""TextBlock"", ""text"": ""{$data}"" } ] } ] } ] } ] } ], ""$schema"": ""http://adaptivecards.io/schemas/adaptive-card.json"", ""version"": ""1.0"" }"; } // How I used to do things with regex: // ========================================================================================================================================== // // get list of {template.refs} from JSON string // Match m = Regex.Match(strTemplate, @"\{(.+)?\}"); // // //List Elements = new List(); // // Dictionary> elements = new Dictionary>(); // // while(m.Success) // { // if (!elements.Keys.Contains(m.Value)) // { // List elementList = new List(); // // find the JToken(s) containing this element // foreach (string el in Globals.CheckElements) // { // IEnumerable tempList = template.SelectTokens("$.." + el).Where(y => y.Value().Contains(m.Value)); // elementList.AddRange(tempList); // } // // // strip {} from element name // string searchTerm = m.Groups[1].Value; // // string searchString = "$." + searchTerm; // // // find the token in Data JSON // // can be JValue or JArray // var searchResult = data.SelectToken(searchString); // // // if this element exists in the data // // and isn't $data // // replace element with text from Data JSON // // otherwise remove it // if (searchResult != null) // { // if (searchResult.GetType() == typeof(JValue)) // { // // it's a JValue // // String.Replace value with SearchResult value as string // // Elements.Add(m.Value, elementList); // // for every element in elementList // // replace m.Value with SearchResult.Value.ToString() // foreach (JValue element in elementList) // { // string oldValue = element.Value(); // string newValue = oldValue.Replace(m.Value,((JValue)searchResult.Value()).ToString()); // element.Value = element.Value.ToString().Replace(oldValue, newValue); // } // } // else if (searchResult.GetType() == typeof(JArray)) // { // // it's a JArray // } // } // else if (m.Value == "{x-data}") // { // // this needs to be output as a list of data // // } // else // { // // what is this? // // find the token in the Template array and remove it // string nodeText = m.Groups[1].Value; // // // search for all nodes containing this value in any of the keys found in CheckElements // // add to list FoundNodes // // build a search string // // // Evaluates to .*\{item\.name}.* // string innerValue = Regex.Escape("{" + nodeText + "}"); // string regexString = $".*{innerValue}.*"; // // string jsonPath = $"..[?(@ =~ /{regexString}/)]"; // // List FoundNodes = template.SelectTokens(jsonPath).ToList(); // // foreach (JToken Node in FoundNodes) // { // // find node by path // // this is the value of the property // // parent is property // // grandparent is node // Node.Parent.Parent.Remove(); // } // } // } // // m = m.NextMatch(); // }