private static JToken Transform(JToken input, JsonTransformerContext context) { if (context.ReservedProperties.Data is JArray dataArray) { if (!context.ParentIsArray) { // If we can't repeat, we select the first item in the array context.Warnings.AddWarning("Current data is an array, but parent item isn't an array. Selecting the first item for current data."); context.ReservedProperties.Data = dataArray.FirstOrDefault(); } } switch (input.Type) { case JTokenType.Array: return(TransformArray(input as JArray, new JsonTransformerContext(context))); case JTokenType.String: return(JsonStringTransformer.Transform(input.Value <string>(), new JsonTransformerContext(context))); case JTokenType.Object: return(TransformObject(input as JObject, new JsonTransformerContext(context)).FirstOrDefault()); } return(input.DeepClone()); }
private static JArray TransformArray(JArray inputArray, JsonTransformerContext context) { var newArray = new JArray(); foreach (var child in inputArray) { if (child is JObject childObj) { var newChildren = TransformObject(childObj, new JsonTransformerContext(context) { ParentIsArray = true }); foreach (var newChild in newChildren) { newArray.Add(newChild); } } else { var newChild = Transform(child, new JsonTransformerContext(context) { ParentIsArray = true }); if (newChild != null) { newArray.Add(newChild); } } } return(newArray); }
/// <summary> /// Evaluates a simple object expression, like "reportsTo.name" or "people[0].name" or even "'Andrew'" or "4" /// </summary> /// <param name="objectExpression"></param> /// <param name="context"></param> /// <returns></returns> private static JToken EvaluateObjectExpression(string objectExpression, JsonTransformerContext context) { Match firstPropertyMatch = Regex.Match(objectExpression, "^" + REGEX_PROPERTY_NAME); if (firstPropertyMatch.Success) { string remainingToParse = objectExpression; JToken data; if (firstPropertyMatch.Value.StartsWith("$")) { data = context.ReservedProperties.GetValue(firstPropertyMatch.Value.Substring(1)); if (data == null) { return(null); } remainingToParse = remainingToParse.Substring(firstPropertyMatch.Length); } else { remainingToParse = "." + remainingToParse; data = context.ReservedProperties.Data; } if (remainingToParse.Length > 0) { data = EvaluateAccessorExpression(remainingToParse, data, context); } return(data); } return(null); }
public JsonTransformerContext(JsonTransformerContext existingContext) { foreach (var p in this.GetType().GetTypeInfo().DeclaredProperties) { p.SetValue(this, p.GetValue(existingContext)); } if (Types != null) { Types = Types.Clone(); } if (ReservedProperties != null) { ReservedProperties = new JsonTransformerReservedProperties(ReservedProperties); } }
private static List <JObject> TransformObject(JObject input, JsonTransformerContext context) { List <JObject> answer = new List <JObject>(); // Clone it since we'll be modifying it input = input.DeepClone() as JObject; // If new custom types are declared if (input.TryGetValue(PROP_TYPES, out JToken typesAsToken)) { input.Remove(PROP_TYPES); try { // We need to obtain them before applying data binding var newTypes = typesAsToken.ToObject <JsonTransformerTypes>(); if (newTypes != null) { context.Types = context.Types.Merge(newTypes); } } catch { } } // If data is specified if (input.TryGetValue(PROP_DATA, out JToken dataVal)) { // Remove the data property input.Remove(PROP_DATA); // Transform and use the data context.ReservedProperties.Data = Transform(dataVal, new JsonTransformerContext(context) { ParentIsArray = false }); // If we couldn't find the data, we drop the entire element if (context.ReservedProperties.Data == null) { return(answer); } } else { // Otherwise, inherit parent's data } // If current data is an array if (context.ReservedProperties.Data != null && context.ReservedProperties.Data is JArray array) { // If our parent is an array type, we repeat if (context.ParentIsArray) { int i = 0; foreach (var dataItem in array) { JObject newRepeatedItem = input.DeepClone() as JObject; newRepeatedItem.Remove(PROP_DATA); foreach (var transformed in TransformObject(newRepeatedItem, new JsonTransformerContext(context) { ReservedProperties = new JsonTransformerReservedProperties(context.ReservedProperties) { Data = dataItem, Index = i } })) { answer.Add(transformed); } i++; } return(answer); } else { context.Warnings.AddWarning("Data was an array on item that isn't a child of an array. Selecting first item of data array."); context.ReservedProperties.Data = array.FirstOrDefault(); } } // Evaluate when clause if (input.TryGetValue(PROP_WHEN, out JToken whenToken)) { input.Remove(PROP_WHEN); var transformedWhenValue = Transform(whenToken, new JsonTransformerContext(context) { ParentIsArray = false }); // If it evaluated to true if (transformedWhenValue != null && transformedWhenValue.Type == JTokenType.Boolean && transformedWhenValue.Value <bool>()) { // Keep it } else { // Otherwise, drop it return(answer); } } var newItem = new JObject(); // Transform each property value foreach (var p in input.Properties().ToArray()) { var transformedPropertyValue = Transform(p.Value, new JsonTransformerContext(context) { ParentIsArray = false }); if (transformedPropertyValue != null) { newItem.Add(p.Name, transformedPropertyValue); } } // If custom type if (newItem.TryGetValue(PROP_TYPE, out JToken typeToken) && typeToken.Type == JTokenType.String) { var typeString = typeToken.Value <string>(); if (context.Types.TryGetDefinition(typeString, out JToken definition) && definition is JObject definitionObj) { foreach (var newTransformedItem in TransformObject(definitionObj, new JsonTransformerContext(context) { ReservedProperties = new JsonTransformerReservedProperties(context.ReservedProperties) { Props = newItem } })) { answer.Add(newTransformedItem); } return(answer); } } answer.Add(newItem); return(answer); }
public static JToken EvaluateBinding(string bindingExpression, JsonTransformerContext context) { string remainingBindingExpression = bindingExpression; string answer = ""; bool first = true; bool found = false; while (true) { int startIndex = remainingBindingExpression.IndexOf('{'); if (startIndex != -1) { string substr = remainingBindingExpression.Substring(startIndex); var inputStream = new AntlrInputStream(substr); var lexer = new BindingExpressionsLexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new BindingExpressionsParser(tokenStream); parser.AddErrorListener(new ErrorListener()); var foundExpression = parser.bindingExpression(); // If it actually found something if (foundExpression.Stop.StopIndex < substr.Length && parser.NumberOfSyntaxErrors == 0) { var visitor = new BindingExpressionsVisitor(context); JToken result = visitor.Visit(foundExpression); answer += remainingBindingExpression.Substring(0, startIndex); answer += result; remainingBindingExpression = substr; remainingBindingExpression = remainingBindingExpression.Substring(foundExpression.Stop.StopIndex + 1); // If whole expression was the binding expression, don't do any string concatenation if (first && startIndex == 0 && remainingBindingExpression.Length == 0) { return(result); } found = true; } else { // Otherwise, increment past there and try again answer += remainingBindingExpression.Substring(startIndex + 1); remainingBindingExpression = remainingBindingExpression.Substring(startIndex + 1); } } else { answer += remainingBindingExpression; break; } first = false; } if (found) { return(answer); } return(bindingExpression); //var inputStream = new AntlrInputStream(bindingExpression); //var lexer = new BindingExpressionsLexer(inputStream); //var tokenStream = new CommonTokenStream(lexer); //var parser = new BindingExpressionsParser(tokenStream); //var foundExpression = parser.input_string(); //if (foundExpression.IsEmpty) //{ // //return bindingExpression; //} //var visitor = new BindingExpressionsVisitor(context); //JToken result = visitor.Visit(foundExpression); //return result; // If it's all one single expression, we don't do any string concatenation and we return the actual type var match = Regex.Match(bindingExpression, "^" + REGEX_BINDING_EXPRESSION + "$"); if (match.Success && match.Groups[1].Success) { return(EvaluateComplexExpression(match.Groups[1].Value, context)); } Regex regex = new Regex(REGEX_BINDING_EXPRESSION); var evaluator = new MyMatchEvaluator(context); string replaced = regex.Replace(bindingExpression, evaluator.ReplaceBinding); return(replaced); }
/// <summary> /// Evaluates full property accessor expressions, like ".person.name" or ".person['First Name']", etc /// </summary> /// <param name="data"></param> /// <param name="propertyExpression"></param> /// <returns></returns> public static JToken EvaluateAccessorExpression(string propertyExpression, JToken currData, JsonTransformerContext context) { if (currData == null) { return(null); } JToken nextData = null; string matchedText = null; Regex regex = new Regex("^" + REGEX_PROPERTY_ACCESSOR); var matchPropertyAccessor = regex.Match(propertyExpression); if (matchPropertyAccessor.Success && matchPropertyAccessor.Groups[1].Success) { string property = matchPropertyAccessor.Groups[1].Value; nextData = GetProperty(currData, property); matchedText = matchPropertyAccessor.Value; } else { regex = new Regex("^" + REGEX_DICTIONARY_LOOKUP); var matchDictionary = regex.Match(propertyExpression); if (matchDictionary.Success && matchDictionary.Groups[1].Success) { string dictionaryAccessor = matchDictionary.Groups[1].Value; nextData = GetProperty(currData, dictionaryAccessor); matchedText = matchDictionary.Value; } else { regex = new Regex("^" + REGEX_INDEX_ACCESSOR); var matchIndex = regex.Match(propertyExpression); if (matchIndex.Success && matchIndex.Groups[1].Success && int.TryParse(matchIndex.Groups[1].Value, out int indexToAccess)) { nextData = currData.ElementAtOrDefault(indexToAccess); matchedText = matchIndex.Value; } } } if (nextData != null) { if (matchedText.Length < propertyExpression.Length) { return(EvaluateAccessorExpression(propertyExpression.Substring(matchedText.Length), nextData, context)); } else { return(nextData); } } return(null); }
public MyMatchEvaluator(JsonTransformerContext context) { _context = context; }
internal static JToken Transform(string input, JsonTransformerContext context) { return(EvaluateBinding(input, context)); }
/// <summary> /// A complex expression, like "person.name", "person['details'].name", "name == 'Andrew'", or even nested complex expressions /// </summary> /// <param name="complexExpression"></param> /// <param name="context"></param> /// <returns></returns> public static JToken EvaluateComplexExpression(string complexExpression, JsonTransformerContext context) { var complexMatch = Regex.Match(complexExpression, "^" + REGEX_COMPLEX_EXPRESSION + "$"); if (complexMatch.Success) { } Match firstPropertyMatch = Regex.Match(complexExpression, "^" + REGEX_PROPERTY_NAME); if (firstPropertyMatch.Success) { string remainingToParse = complexExpression; JToken data; if (firstPropertyMatch.Value.StartsWith("$")) { data = context.ReservedProperties.GetValue(firstPropertyMatch.Value.Substring(1)); if (data == null) { return(null); } remainingToParse = remainingToParse.Substring(firstPropertyMatch.Length); } else { remainingToParse = "." + remainingToParse; data = context.ReservedProperties.Data; } if (remainingToParse.Length > 0) { data = EvaluateAccessorExpression(remainingToParse, data, context); } string remainingExpression = complexExpression.Substring(firstPropertyMatch.Value.Length); // See if there's a binary operator following this object var regexOperator = new Regex("^" + REGEX_OPERATOR); var matchOperator = regexOperator.Match(remainingExpression); if (matchOperator.Success && matchOperator.Groups[1].Success) { string operatorStr = matchOperator.Groups[1].Value; // Get the second object of the binary operation // Note this is slightly incorrect, since it'll join chained operations from the end to the front. For example, // true == 'Andrew' == 'Andrew' would evaluate to true, as (true == ('Andrew' == 'Andrew')), but it should be the other way around // Plus I'm temporarily hacking this by appending { at the start, need to split this out into different methods better JToken secondObject = EvaluateComplexExpression("{" + remainingExpression.Substring(matchOperator.Value.Length), context); switch (operatorStr) { case "==": return(object.Equals(data, secondObject)); } } return(data); } return(null); }