diff --git a/JUST.net/DataTransformer.cs b/JUST.net/DataTransformer.cs index 10ba024..f4d254d 100644 --- a/JUST.net/DataTransformer.cs +++ b/JUST.net/DataTransformer.cs @@ -161,8 +161,6 @@ private object EvaluateFunction(string functionString, string inputJson, JArray } } - listParameters.Add(new JUSTContext(inputJson)); - var parameters = listParameters.ToArray(); var convertParameters = true; if (new[] { "concat", "xconcat", "currentproperty" }.Contains(functionName)) { @@ -171,7 +169,7 @@ private object EvaluateFunction(string functionString, string inputJson, JArray if (functionName == "loop") { - output = GetLoopResult(parameters, loopArgumentString); + output = GetLoopResult(listParameters.Concat(new object[] { JToken.Parse(inputJson), Context }).ToArray(), loopArgumentString, Context); } else if (functionName == "currentvalue" || functionName == "currentindex" || functionName == "lastindex" || functionName == "lastvalue") @@ -179,11 +177,11 @@ private object EvaluateFunction(string functionString, string inputJson, JArray else if (functionName == "currentvalueatpath" || functionName == "lastvalueatpath") output = Caller("JUST.Transformer`1", functionName, new object[] { array, currentArrayElement, arguments[0], new JUSTContext() }); else if (functionName == "customfunction") - output = CallCustomFunction(parameters); + output = CallCustomFunction(listParameters.Concat(new object[] { JToken.Parse(inputJson), Context }).ToArray()); else if (Context?.IsRegisteredCustomFunction(functionName) ?? false) { var methodInfo = Context.GetCustomMethod(functionName); - output = ReflectionHelper.InvokeCustomMethod(methodInfo, parameters, convertParameters, Context.EvaluationMode); + output = ReflectionHelper.InvokeCustomMethod(methodInfo, listParameters.Concat(new object[] { JToken.Parse(inputJson), Context }).ToArray(), convertParameters, Context); } else if (functionName == "xconcat" || functionName == "xadd" || functionName == "mathequals" || functionName == "mathgreaterthan" || functionName == "mathlessthan" || functionName == "mathgreaterthanorequalto" @@ -191,24 +189,24 @@ private object EvaluateFunction(string functionString, string inputJson, JArray functionName == "stringequals") { object[] oParams = new object[1]; - oParams[0] = parameters; + oParams[0] = listParameters.Concat(new object[] { Context }).ToArray(); output = Caller("JUST.Transformer`1", functionName, oParams); } else - output = Caller("JUST.Transformer`1", functionName, parameters); + output = Caller("JUST.Transformer`1", functionName, listParameters.Concat(new object[] { JToken.Parse(inputJson), Context }).ToArray()); } return output; } - private string GetLoopResult(object[] parameters,string loopArgumentString) + private string GetLoopResult(object[] parameters,string loopArgumentString, JUSTContext context) { string returnString = string.Empty; - if (parameters.Length < 2) + if (parameters.Length < 3) throw new Exception("Incorrect number of parameters for function #Loop"); - var context = (JUSTContext)parameters[parameters.Length - 1]; - JToken token = context.Input; + //var context = (JUSTContext)parameters[parameters.Length - 1]; + JToken token = (JToken)parameters[parameters.Length - 2]; JToken selectedToken = context.Resolve(token).Select(parameters[0].ToString()); if (selectedToken.Type != JTokenType.Array) @@ -218,7 +216,7 @@ private string GetLoopResult(object[] parameters,string loopArgumentString) string seperator = Environment.NewLine; - if (parameters.Length == 3) + if (parameters.Length == 4) seperator = parameters[1].ToString(); foreach (JToken arrToken in selectedToken.Children()) diff --git a/JUST.net/ExceptionHelper.cs b/JUST.net/ExceptionHelper.cs new file mode 100644 index 0000000..f79e9eb --- /dev/null +++ b/JUST.net/ExceptionHelper.cs @@ -0,0 +1,19 @@ +using System; + +namespace JUST +{ + internal static class ExceptionHelper + { + internal static void HandleException(Exception ex, bool IsStrictMode) + { + if (IsStrictMode) + { + if (ex.InnerException != null) + { + throw ex.InnerException; + }; + throw ex; + } + } + } +} diff --git a/JUST.net/IContext.cs b/JUST.net/IContext.cs new file mode 100644 index 0000000..f76d27e --- /dev/null +++ b/JUST.net/IContext.cs @@ -0,0 +1,10 @@ +using JUST; +using JUST.net.Selectables; +using Newtonsoft.Json.Linq; + +public interface IContext{ + char SplitGroupChar { get; } + int DefaultDecimalPlaces { get; } + bool IsStrictMode(); + T Resolve(JToken token) where T: ISelectableToken; +} \ No newline at end of file diff --git a/JUST.net/JUSTContext.cs b/JUST.net/JUSTContext.cs index 651eac4..42ea753 100644 --- a/JUST.net/JUSTContext.cs +++ b/JUST.net/JUSTContext.cs @@ -36,15 +36,13 @@ public enum EvaluationMode : short LookInTransformed = 16 } - public class JUSTContext + public class JUSTContext : IContext { private Dictionary _customFunctions = new Dictionary(); private int _defaultDecimalPlaces = 28; private char _escapeChar = '/'; //do not use backslash, it is already the escape char in JSON private char _splitGroupChar = ':'; - internal JToken Input; - public EvaluationMode EvaluationMode = EvaluationMode.FallbackToDefault; public JsonSerializerSettings JsonSettings { get; set; } @@ -92,12 +90,16 @@ public JUSTContext(IEnumerable customFunctions) } } - internal JUSTContext(string inputJson) + internal JUSTContext(JUSTContext context) { - Input = JToken.Parse(inputJson); + this.EvaluationMode = context.EvaluationMode; + this.EscapeChar = context.EscapeChar; + this.DefaultDecimalPlaces = context.DefaultDecimalPlaces; + this.SplitGroupChar = context.SplitGroupChar; + this._customFunctions = context._customFunctions; } - internal bool IsStrictMode() + public bool IsStrictMode() { return (EvaluationMode & EvaluationMode.Strict) == EvaluationMode.Strict; } @@ -157,7 +159,7 @@ internal bool IsRegisteredCustomFunction(string aliasOrName) return _customFunctions.ContainsKey(aliasOrName); } - internal T Resolve(JToken token) where T : ISelectableToken + public T Resolve(JToken token) where T: ISelectableToken { T instance = Activator.CreateInstance(); instance.Token = token; diff --git a/JUST.net/JsonTransformer.cs b/JUST.net/JsonTransformer.cs index 3a97c6f..4dde992 100644 --- a/JUST.net/JsonTransformer.cs +++ b/JUST.net/JsonTransformer.cs @@ -105,9 +105,8 @@ public JToken Transform(JObject transformer, string input) public JToken Transform(JObject transformer, JToken input) { - Context.Input = input; var parentToken = (JToken)transformer; - State state = new State(transformer, Context.Input, _levelCounter); + State state = new State(transformer, input); RecursiveEvaluate(ref parentToken, state); return parentToken; } @@ -124,61 +123,40 @@ private void RecursiveEvaluate(ref JToken parentToken, State state) JEnumerable tokens = parentToken.Children(); - List selectedTokens = null; - Dictionary tokensToReplace = null; - List tokensToDelete = null; - - List loopProperties = null; - List scopeProperties = null; - List condProps = null; - JArray arrayToForm = null; - JObject dictToForm = null; - JObject scopeToForm = null; - List tokenToForm = null; - List tokensToAdd = null; - - bool isLoop = false; - bool isBulk = false; - bool isScope = false; - + TransformHelper helper = new TransformHelper(); foreach (JToken childToken in tokens) { - ParseToken(ref parentToken, state, ref selectedTokens, ref tokensToReplace, ref tokensToDelete, ref loopProperties, ref condProps, ref arrayToForm, ref dictToForm, ref scopeProperties, ref scopeToForm, ref tokenToForm, ref tokensToAdd, ref isLoop, ref isBulk, ref isScope, childToken); + ParseToken(parentToken, state, helper, childToken); } - if (selectedTokens != null) + if (helper.selectedTokens != null) { - CopyPostOperationBuildUp(parentToken, selectedTokens, this.Context); + CopyPostOperationBuildUp(parentToken, helper.selectedTokens); } - if (tokensToReplace != null) + if (helper.tokensToReplace != null) { - ReplacePostOperationBuildUp(parentToken, tokensToReplace, this.Context); + ReplacePostOperationBuildUp(parentToken, helper.tokensToReplace); } - if (tokensToDelete != null) + if (helper.tokensToDelete != null) { - DeletePostOperationBuildUp(parentToken, tokensToDelete, this.Context); + DeletePostOperationBuildUp(parentToken, helper.tokensToDelete); } - if (tokensToAdd != null) + if (helper.tokensToAdd != null) { - AddPostOperationBuildUp(parentToken, tokensToAdd); + AddPostOperationBuildUp(parentToken, helper.tokensToAdd); } - PostOperationsBuildUp(ref parentToken, tokenToForm); - if (loopProperties != null || condProps != null) + PostOperationsBuildUp(ref parentToken, helper.tokenToForm); + if (helper.loopProperties != null || helper.condProps != null) { - LoopPostOperationBuildUp(ref parentToken, condProps, loopProperties, arrayToForm, dictToForm); + LoopPostOperationBuildUp(ref parentToken, helper); } - if (scopeToForm != null) + if (helper.scopeToForm != null) { - ScopePostOperationBuildUp(ref parentToken, scopeProperties, scopeToForm); + ScopePostOperationBuildUp(ref parentToken, helper.scopeProperties, helper.scopeToForm); } } - private void ParseToken(ref JToken parentToken, State state, - ref List selectedTokens, ref Dictionary tokensToReplace, ref List tokensToDelete, - ref List loopProperties, ref List condProps, ref JArray arrayToForm, ref JObject dictToForm, - ref List scopeProperties, ref JObject scopeToForm, - ref List tokenToForm, ref List tokensToAdd, - ref bool isLoop, ref bool isBulk, ref bool isScope, JToken childToken) + private void ParseToken(JToken parentToken, State state, TransformHelper helper, JToken childToken) { if (childToken.Type == JTokenType.Array && (parentToken as JProperty)?.Name.Trim() != "#") { @@ -188,19 +166,19 @@ private void ParseToken(ref JToken parentToken, State state, else if (childToken.Type == JTokenType.Property && childToken is JProperty property && property.Name != null) { /* For looping*/ - isLoop = false; + helper.isLoop = false; if (property.Name == "#" && property.Value.Type == JTokenType.Array && property.Value is JArray values) { - BulkOperations(values.Children(), state, ref selectedTokens, ref tokensToReplace, ref tokensToDelete); - isBulk = true; + BulkOperations(values.Children(), state, helper); + helper.isBulk = true; } else { - isBulk = false; + helper.isBulk = false; if (ExpressionHelper.TryParseFunctionNameAndArguments(property.Name, out string functionName, out string arguments)) { - ParsePropertyFunction(state, ref loopProperties, ref condProps, ref arrayToForm, ref dictToForm, ref scopeProperties, ref scopeToForm, ref tokenToForm, ref tokensToAdd, ref isLoop, ref isScope, childToken, property, functionName, arguments); + ParsePropertyFunction(state, helper, childToken, property, functionName, arguments); } else if (property.Value.ToString().Trim().StartsWith("#")) { @@ -226,34 +204,34 @@ private void ParseToken(ref JToken parentToken, State state, childToken.Replace(GetToken(newValue)); } - if (!isLoop && !isBulk && !isScope) + if (!helper.isLoop && !helper.isBulk && !helper.isScope) { RecursiveEvaluate(ref childToken, state); } } - private void ParsePropertyFunction(State state, ref List loopProperties, ref List condProps, ref JArray arrayToForm, ref JObject dictToForm, ref List scopeProperties, ref JObject scopeToForm, ref List tokenToForm, ref List tokensToAdd, ref bool isLoop, ref bool isScope, JToken childToken, JProperty property, string functionName, string arguments) + private void ParsePropertyFunction(State state, TransformHelper helper, JToken childToken, JProperty property, string functionName, string arguments) { switch (functionName) { case "ifgroup": - ConditionalGroupOperation(property.Name, arguments, state, ref condProps, ref tokenToForm, childToken); + ConditionalGroupOperation(property.Name, arguments, state, helper, childToken); break; case "loop": - LoopOperation(property.Name, arguments, state, ref loopProperties, ref arrayToForm, ref dictToForm, childToken); - isLoop = true; + LoopOperation(property.Name, arguments, state, helper, childToken); + helper.isLoop = true; break; case "eval": - EvalOperation(property, arguments, state, ref loopProperties, ref tokensToAdd); + EvalOperation(property, arguments, state, helper); break; case "scope": - ScopeOperation(property.Name, arguments, state, ref scopeProperties, ref scopeToForm, childToken); - isScope = true; + ScopeOperation(property.Name, arguments, state, helper, childToken); + helper.isScope = true; break; } } - private void PostOperationsBuildUp(ref JToken parentToken, List tokenToForm) + private void PostOperationsBuildUp(ref JToken parentToken, IList tokenToForm) { if (tokenToForm != null) { @@ -285,7 +263,7 @@ private void PostOperationsBuildUp(ref JToken parentToken, List tokenToF } } - private static void CopyPostOperationBuildUp(JToken parentToken, List selectedTokens, JUSTContext context) + private void CopyPostOperationBuildUp(JToken parentToken, IList selectedTokens) { foreach (JToken selectedToken in selectedTokens) { @@ -293,7 +271,7 @@ private static void CopyPostOperationBuildUp(JToken parentToken, List se { JObject parent = parentToken as JObject; JEnumerable copyChildren = selectedToken.Children(); - if (context.IsAddOrReplacePropertiesMode()) + if (Context.IsAddOrReplacePropertiesMode()) { CopyDescendants(parent, copyChildren); } @@ -344,7 +322,7 @@ private static void CopyDescendants(JObject parent, JEnumerable children } } - private static void AddPostOperationBuildUp(JToken parentToken, List tokensToAdd) + private static void AddPostOperationBuildUp(JToken parentToken, IList tokensToAdd) { if (tokensToAdd != null) { @@ -355,39 +333,38 @@ private static void AddPostOperationBuildUp(JToken parentToken, List tok } } - private static void DeletePostOperationBuildUp(JToken parentToken, List tokensToDelete, JUSTContext context) + private void DeletePostOperationBuildUp(JToken parentToken, IList tokensToDelete) { foreach (string selectedToken in tokensToDelete) { - JToken tokenToRemove = GetSelectableToken(parentToken, context).Select(selectedToken); + JToken tokenToRemove = GetSelectableToken(parentToken, Context).Select(selectedToken); if (tokenToRemove != null) tokenToRemove.Ancestors().First().Remove(); } - } - private static void ReplacePostOperationBuildUp(JToken parentToken, Dictionary tokensToReplace, JUSTContext context) + private void ReplacePostOperationBuildUp(JToken parentToken, IDictionary tokensToReplace) { foreach (KeyValuePair tokenToReplace in tokensToReplace) { - JsonPathSelectable selectable = JsonTransformer.GetSelectableToken(parentToken, context); + JsonPathSelectable selectable = JsonTransformer.GetSelectableToken(parentToken, Context); JToken selectedToken = selectable.Select(tokenToReplace.Key); selectedToken.Replace(tokenToReplace.Value); } } - private static void LoopPostOperationBuildUp(ref JToken parentToken, List condProps, List loopProperties, JArray arrayToForm, JObject dictToForm) + private static void LoopPostOperationBuildUp(ref JToken parentToken, TransformHelper helper) { - if (loopProperties != null) + if (helper.loopProperties != null) { if (parentToken is JObject obj) { - foreach (string propertyToDelete in loopProperties) + foreach (string propertyToDelete in helper.loopProperties) { - if (dictToForm == null && arrayToForm == null && parentToken.Count() <= 1) + if (helper.dictToForm == null && helper.arrayToForm == null && parentToken.Count() <= 1) { obj.Replace(JValue.CreateNull()); } @@ -399,28 +376,28 @@ private static void LoopPostOperationBuildUp(ref JToken parentToken, List scopeProperties, JObject scopeToForm) + private static void ScopePostOperationBuildUp(ref JToken parentToken, IList scopeProperties, JObject scopeToForm) { if (parentToken is JObject obj) { @@ -466,18 +443,18 @@ private static void ScopePostOperationBuildUp(ref JToken parentToken, List loopProperties, ref JArray arrayToForm, ref JObject dictToForm, JToken childToken) + private void LoopOperation(string propertyName, string arguments, State state, TransformHelper helper, JToken childToken) { var args = ExpressionHelper.SplitArguments(arguments, Context.EscapeChar); var previousAlias = "root"; args[0] = (string)ParseFunction(args[0], state); - _levelCounter++; - string alias = args.Length > 1 ? (string)ParseFunction(args[1].Trim(), state) : $"loop{_levelCounter}"; + state.LevelCounter++; + string alias = args.Length > 1 ? (string)ParseFunction(args[1].Trim(), state) : $"loop{state.LevelCounter}"; if (args.Length > 2) { previousAlias = (string)ParseFunction(args[2].Trim(), state); - state.CurrentArrayToken.Add(new LevelKey() { Key = previousAlias, Level = _levelCounter }, Context.Input); + //state.CurrentArrayToken.Add(new LevelKey() { Key = previousAlias, Level = _levelCounter }, state.GetInput()); } else if (state.CurrentArrayToken.Any(t => t.Key.Key == alias)) { @@ -524,11 +501,11 @@ private void LoopOperation(string propertyName, string arguments, State state, r { using (IEnumerator elements = array.GetEnumerator()) { - state.ParentArray.Add(new LevelKey { Level = _levelCounter, Key = alias}, array); - - if (arrayToForm == null) + state.ParentArray.Add(new LevelKey { Level = state.LevelCounter, Key = alias}, array); + + if (helper.arrayToForm == null) { - arrayToForm = new JArray(); + helper.arrayToForm = new JArray(); } if (!isDictionary) { @@ -537,46 +514,46 @@ private void LoopOperation(string propertyName, string arguments, State state, r JToken clonedToken = childToken.DeepClone(); if (state.CurrentArrayToken.Any(a => a.Key.Key == alias)) { - state.CurrentArrayToken.Remove(new LevelKey { Level = _levelCounter, Key = alias}); + state.CurrentArrayToken.Remove(new LevelKey { Level = state.LevelCounter, Key = alias}); } - state.CurrentArrayToken.Add(new LevelKey { Level = _levelCounter, Key = alias}, elements.Current); + state.CurrentArrayToken.Add(new LevelKey { Level = state.LevelCounter, Key = alias}, elements.Current); RecursiveEvaluate(ref clonedToken, state); foreach (JToken replacedProperty in clonedToken.Children()) { - arrayToForm.Add(replacedProperty.Type != JTokenType.Null ? replacedProperty : new JObject()); + helper.arrayToForm.Add(replacedProperty.Type != JTokenType.Null ? replacedProperty : new JObject()); } } } else { - dictToForm = new JObject(); + helper.dictToForm = new JObject(); while (elements.MoveNext()) { JToken clonedToken = childToken.DeepClone(); if (state.CurrentArrayToken.Any(a => a.Key.Key == alias)) { - state.CurrentArrayToken.Remove(new LevelKey { Level = _levelCounter, Key = alias}); + state.CurrentArrayToken.Remove(new LevelKey { Level = state.LevelCounter, Key = alias}); } - state.CurrentArrayToken.Add(new LevelKey { Level = _levelCounter, Key = alias}, elements.Current); + state.CurrentArrayToken.Add(new LevelKey { Level = state.LevelCounter, Key = alias}, elements.Current); RecursiveEvaluate(ref clonedToken, state); foreach (JToken replacedProperty in clonedToken.Children().Select(t => t.First)) { - dictToForm.Add(replacedProperty); + helper.dictToForm.Add(replacedProperty); } } } - state.ParentArray.Remove(new LevelKey { Level = _levelCounter, Key = alias}); - state.CurrentArrayToken.Remove(new LevelKey { Level = _levelCounter, Key = alias}); + state.ParentArray.Remove(new LevelKey { Level = state.LevelCounter, Key = alias}); + state.CurrentArrayToken.Remove(new LevelKey { Level = state.LevelCounter, Key = alias}); } } } - if (loopProperties == null) - loopProperties = new List(); + if (helper.loopProperties == null) + helper.loopProperties = new List(); - loopProperties.Add(propertyName); - _levelCounter--; + helper.loopProperties.Add(propertyName); + state.LevelCounter--; } private bool IsArray(JToken arrayToken, string strArrayToken, State state, string alias) @@ -584,13 +561,13 @@ private bool IsArray(JToken arrayToken, string strArrayToken, State state, strin return typeof(T) == typeof(JsonPathSelectable) && arrayToken.Type != JTokenType.Array && (Regex.IsMatch(strArrayToken ?? string.Empty, "\\[.+\\]$") || (state.CurrentArrayToken != null && state.CurrentArrayToken.Any(a => a.Key.Key == alias) && state.CurrentArrayToken.Single(a => a.Key.Key == alias).Value != null && Regex.IsMatch(state.CurrentArrayToken.Single(a => a.Key.Key == alias).Value.Value(), "\\[.+\\]$"))); } - private void ScopeOperation(string propertyName, string arguments, State state, ref List scopeProperties, ref JObject scopeToForm, JToken childToken) + private void ScopeOperation(string propertyName, string arguments, State state, TransformHelper helper, JToken childToken) { var args = ExpressionHelper.SplitArguments(arguments, Context.EscapeChar); var previousAlias = "root"; args[0] = (string)ParseFunction(args[0], state); - _levelCounter++; - string alias = args.Length > 1 ? (string)ParseFunction(args[1].Trim(), state) : $"scope{_levelCounter}"; + state.LevelCounter++; + string alias = args.Length > 1 ? (string)ParseFunction(args[1].Trim(), state) : $"scope{state.LevelCounter}"; if (args.Length > 2) { @@ -610,28 +587,28 @@ private void ScopeOperation(string propertyName, string arguments, State state, JToken clonedToken = childToken.DeepClone(); if (state.CurrentScopeToken.Any(s => s.Key.Key == alias)) { - state.CurrentScopeToken.Remove(new LevelKey {Level = _levelCounter, Key = alias}); + state.CurrentScopeToken.Remove(new LevelKey {Level = state.LevelCounter, Key = alias}); } - state.CurrentScopeToken.Add(new LevelKey {Level = _levelCounter, Key = alias}, scopeToken); + state.CurrentScopeToken.Add(new LevelKey {Level = state.LevelCounter, Key = alias}, scopeToken); RecursiveEvaluate(ref clonedToken, state); - scopeToForm = clonedToken.Children().First().Value(); + helper.scopeToForm = clonedToken.Children().First().Value(); - state.CurrentScopeToken.Remove(new LevelKey {Level = _levelCounter, Key = alias}); + state.CurrentScopeToken.Remove(new LevelKey {Level = state.LevelCounter, Key = alias}); - if (scopeProperties == null) - scopeProperties = new List(); + if (helper.scopeProperties == null) + helper.scopeProperties = new List(); - scopeProperties.Add(propertyName); - _levelCounter--; + helper.scopeProperties.Add(propertyName); + state.LevelCounter--; } - private void ConditionalGroupOperation(string propertyName, string arguments, State state, ref List condProps, ref List tokenToForm, JToken childToken) + private void ConditionalGroupOperation(string propertyName, string arguments, State state, TransformHelper helper, JToken childToken) { object functionResult = ParseFunction(arguments, state); bool result; try { - result = (bool)ReflectionHelper.GetTypedValue(typeof(bool), functionResult, Context.EvaluationMode); + result = (bool)ReflectionHelper.GetTypedValue(typeof(bool), functionResult, Context.IsStrictMode()); } catch { @@ -641,36 +618,36 @@ private void ConditionalGroupOperation(string propertyName, string arguments, St if (result) { - if (condProps == null) - condProps = new List(); + if (helper.condProps == null) + helper.condProps = new List(); - condProps.Add(propertyName); + helper.condProps.Add(propertyName); RecursiveEvaluate(ref childToken, state); - if (tokenToForm == null) + if (helper.tokenToForm == null) { - tokenToForm = new List(); + helper.tokenToForm = new List(); } foreach (JToken grandChildToken in childToken.Children()) { - tokenToForm.Add(grandChildToken.DeepClone()); + helper.tokenToForm.Add(grandChildToken.DeepClone()); } } else { - if (condProps == null) + if (helper.condProps == null) { - condProps = new List(); + helper.condProps = new List(); } - condProps.Add(propertyName); + helper.condProps.Add(propertyName); childToken.First.Replace(JToken.Parse("{}")); } } - private void EvalOperation(JProperty property, string arguments, State state, ref List loopProperties, ref List tokensToAdd) + private void EvalOperation(JProperty property, string arguments, State state, TransformHelper helper) { object functionResult = ParseFunction(arguments, state); @@ -688,19 +665,19 @@ private void EvalOperation(JProperty property, string arguments, State state, re JProperty clonedProperty = new JProperty(functionResult.ToString(), val); - if (loopProperties == null) - loopProperties = new List(); + if (helper.loopProperties == null) + helper.loopProperties = new List(); - loopProperties.Add(property.Name); + helper.loopProperties.Add(property.Name); - if (tokensToAdd == null) + if (helper.tokensToAdd == null) { - tokensToAdd = new List(); + helper.tokensToAdd = new List(); } - tokensToAdd.Add(clonedProperty); + helper.tokensToAdd.Add(clonedProperty); } - private void BulkOperations(JEnumerable arrayValues, State state, ref List selectedTokens, ref Dictionary tokensToReplace, ref List tokensToDelete) + private void BulkOperations(JEnumerable arrayValues, State state, TransformHelper helper) { foreach (JToken arrayValue in arrayValues) { @@ -710,24 +687,24 @@ private void BulkOperations(JEnumerable arrayValues, State state, ref Li { if (functionName == "copy") { - if (selectedTokens == null) - selectedTokens = new List(); - selectedTokens.Add(Copy(arguments, state)); + if (helper.selectedTokens == null) + helper.selectedTokens = new List(); + helper.selectedTokens.Add(Copy(arguments, state)); } else if (functionName == "replace") { - if (tokensToReplace == null) - tokensToReplace = new Dictionary(); + if (helper.tokensToReplace == null) + helper.tokensToReplace = new Dictionary(); var replaceResult = Replace(arguments, state); - tokensToReplace.Add(replaceResult.Key, replaceResult.Value); + helper.tokensToReplace.Add(replaceResult.Key, replaceResult.Value); } else if (functionName == "delete") { - if (tokensToDelete == null) - tokensToDelete = new List(); + if (helper.tokensToDelete == null) + helper.tokensToDelete = new List(); - tokensToDelete.Add(Delete(arguments, state)); + helper.tokensToDelete.Add(Delete(arguments, state)); } } } @@ -836,7 +813,7 @@ private JToken Copy(string arguments, State state) throw new ArgumentException($"Unknown loop alias: '{argumentArr[1]}'"); } } - JToken input = alias != null ? state.CurrentArrayToken.Single(a => a.Key.Key == alias).Value : state.CurrentArrayToken.Last().Value ?? Context.Input; + JToken input = alias != null ? state.CurrentArrayToken.Single(a => a.Key.Key == alias).Value : state.CurrentArrayToken.Last().Value ?? state.GetInput(); JToken selectedToken = GetSelectableToken(input, Context).Select(jsonPath); return selectedToken; } @@ -901,8 +878,7 @@ private object ParseFunction(string functionString, State state) output = LookInTransformed(output, arguments[i], state); listParameters.Add(output); } - listParameters.Add(Context); - + var convertParameters = true; if (new[] { "concat", "xconcat", "currentproperty" }.Contains(functionName)) { @@ -917,13 +893,14 @@ private object ParseFunction(string functionString, State state) catch (Exception ex) { throw new Exception("Error while calling function : " + functionString + " - " + ex.Message, ex); + // throw; } } private object ParseApplyOver(IList listParameters, State state) { object output; - JToken tmpContext = Context.Input, contextInput = Context.Input; + JToken tmpContext = state.GetInput(), contextInput = state.GetInput(); if (state.ParentArray.Any()) { var alias = ParseLoopAlias(listParameters, 3, state.ParentArray.Last().Key.Key); @@ -931,7 +908,7 @@ private object ParseApplyOver(IList listParameters, State state) } var input = JToken.Parse(Transform(listParameters.ElementAt(0).ToString(), contextInput.ToString())); - Context.Input = input; + //Context.Input = input; IDictionary tmpArray = new Dictionary(state.CurrentArrayToken); IDictionary tmpScope = new Dictionary(state.CurrentScopeToken); @@ -956,7 +933,7 @@ private object ParseApplyOver(IList listParameters, State state) { output = ParseFunction(listParameters.ElementAt(1).ToString().Trim().Trim('\''), state); } - Context.Input = tmpContext; + // Context.Input = tmpContext; state.CurrentArrayToken.Clear(); foreach (KeyValuePair item in tmpArray) @@ -975,7 +952,7 @@ private object ParseApplyOver(IList listParameters, State state) private string ParseLoopAlias(IList listParameters, int index, string defaultValue) { string alias; - if (listParameters != null && listParameters.Count > index) + if (listParameters != null && listParameters.Count >= index) { alias = (listParameters[index - 1] as string).Trim(); listParameters.RemoveAt(index - 1); @@ -1017,7 +994,7 @@ private object GetConditionalOutput(string[] arguments, State state) private object GetFunctionOutput(string functionName, IList listParameters, bool convertParameters, State state) { - object output = null; + object output; if (new[] { "currentvalue", "currentindex", "lastindex", "lastvalue" }.Contains(functionName)) { var alias = ParseLoopAlias(listParameters, 1, state.ParentArray.Last().Key.Key); @@ -1030,7 +1007,9 @@ private object GetFunctionOutput(string functionName, IList listParamete null, "JUST.Transformer`1", functionName, - new[] { state.ParentArray.Single(p => p.Key.Key == alias).Value, state.CurrentArrayToken.Single(p => p.Key.Key == alias).Value }.Concat(listParameters.ToArray()).ToArray(), + new[] { state.ParentArray.Single(p => p.Key.Key == alias).Value, state.CurrentArrayToken.Single(p => p.Key.Key == alias).Value } + .Concat(listParameters.ToArray() + .Concat(new object[] { Context })).ToArray(), convertParameters, Context); } @@ -1042,14 +1021,19 @@ private object GetFunctionOutput(string functionName, IList listParamete convertParameters, Context); } else if (functionName == "customfunction") + { + listParameters.Add(state.CurrentScopeToken.Last().Value); output = CallCustomFunction(listParameters.ToArray()); + } else if (Context.IsRegisteredCustomFunction(functionName)) { var methodInfo = Context.GetCustomMethod(functionName); - output = ReflectionHelper.InvokeCustomMethod(methodInfo, listParameters.ToArray(), convertParameters, Context.EvaluationMode); + listParameters.Add(state.CurrentScopeToken.Last().Value); + output = ReflectionHelper.InvokeCustomMethod(methodInfo, listParameters.ToArray(), convertParameters, Context); } else if (Regex.IsMatch(functionName, ReflectionHelper.EXTERNAL_ASSEMBLY_REGEX)) { + listParameters.Add(state.CurrentScopeToken.Last().Value); output = ReflectionHelper.CallExternalAssembly(functionName, listParameters.ToArray(), Context); } else if (new[] { "xconcat", "xadd", @@ -1057,7 +1041,7 @@ private object GetFunctionOutput(string functionName, IList listParamete "stringcontains", "stringequals"}.Contains(functionName)) { object[] oParams = new object[1]; - oParams[0] = listParameters.ToArray(); + oParams[0] = listParameters.Concat(new object[] { Context }).ToArray(); output = ReflectionHelper.Caller(null, "JUST.Transformer`1", functionName, oParams, convertParameters, Context); } else if (functionName == "applyover") @@ -1066,33 +1050,36 @@ private object GetFunctionOutput(string functionName, IList listParamete } else { - var input = ((JUSTContext)listParameters.Last()).Input; + var input = state.GetAliasToken(state.GetHigherAlias()); //((JUSTContext)listParameters.Last()).Input; if (input != state.Transformer) { if (functionName != "valueof") { - ((JUSTContext)listParameters.Last()).Input = state.CurrentArrayToken.Last().Value; + //((JUSTContext)listParameters.Last()).Input = state.CurrentArrayToken.Last().Value; + listParameters.Add(state.CurrentArrayToken.Last().Value); + } + else if (listParameters.Count >= 2) + { + //((JUSTContext)listParameters.Last()).Input = state.CurrentScopeToken.Single(p => p.Key.Key == listParameters[1].ToString()).Value; + listParameters.Add(state.CurrentScopeToken.Single(p => p.Key.Key == listParameters[1].ToString()).Value); + listParameters.Remove(listParameters.ElementAt(listParameters.Count - 2)); } else { - if (functionName == "valueof" && listParameters.Count > 2) - { - ((JUSTContext)listParameters.Last()).Input = state.CurrentScopeToken.Single(p => p.Key.Key == listParameters[1].ToString()).Value; - listParameters.Remove(listParameters.ElementAt(listParameters.Count - 2)); - } - else - { - ((JUSTContext)listParameters.Last()).Input = state.CurrentScopeToken.Last().Value; - } + //((JUSTContext)listParameters.Last()).Input = state.CurrentScopeToken.Last().Value; + listParameters.Add(state.CurrentScopeToken.Last().Value); } } - - output = ReflectionHelper.Caller(null, "JUST.Transformer`1", functionName, listParameters.ToArray(), convertParameters, Context); - - if (input != state.Transformer) + else { - ((JUSTContext)listParameters.Last()).Input = input; + //((JUSTContext)listParameters.Last()).Input = state.CurrentScopeToken.Last().Value; + listParameters.Add(state.CurrentScopeToken.Last().Value); } + + //listParameters.Add(state.GetInput()); + listParameters.Add(Context); + + output = ReflectionHelper.Caller(null, "JUST.Transformer`1", functionName, listParameters.ToArray(), convertParameters, Context); } return output; } @@ -1101,15 +1088,19 @@ private object LookInTransformed(object output, string propVal, State state) { if (output == null && Context.IsLookInTransformed()) { - JToken tmpContext = Context.Input; - Context.Input = state.Transformer.Root; + state.LevelCounter++; + state.CurrentScopeToken.Add(new LevelKey { Key = $"transformed{state.LevelCounter}", Level = state.LevelCounter }, state.Transformer.Root); + //JToken tmpContext = state.GetInput(); //Context.Input; + //Context.Input = state.Transformer.Root; output = ParseFunction(propVal, state); - Context.Input = tmpContext; + //Context.Input = tmpContext; + state.CurrentScopeToken.Remove(state.CurrentScopeToken.Single(s => s.Key.Key == $"transformed{state.LevelCounter}")); + state.LevelCounter--; } return output; } - private object CallCustomFunction(object[] parameters) + private object CallCustomFunction(object[] parameters) { object[] customParameters = new object[parameters.Length - 3]; string functionString = string.Empty; diff --git a/JUST.net/ReflectionHelper.cs b/JUST.net/ReflectionHelper.cs index 82c5e9e..cab711d 100644 --- a/JUST.net/ReflectionHelper.cs +++ b/JUST.net/ReflectionHelper.cs @@ -15,7 +15,7 @@ internal static class ReflectionHelper { internal const string EXTERNAL_ASSEMBLY_REGEX = "([\\w.]+)[:]{2}([\\w.]+)[:]{0,2}([\\w.]*)"; - internal static object Caller(Assembly assembly, string myclass, string mymethod, object[] parameters, bool convertParameters, JUSTContext context) where T : ISelectableToken + internal static object Caller(Assembly assembly, string myclass, string mymethod, object[] parameters, bool convertParameters, IContext context) where T : ISelectableToken { Type type = assembly?.GetType(myclass) ?? Type.GetType(myclass); if (type?.ContainsGenericParameters ?? false) @@ -29,7 +29,7 @@ internal static object Caller(Assembly assembly, string myclass, string mymet } try { - return InvokeCustomMethod(methodInfo, parameters, convertParameters, context.EvaluationMode); + return InvokeCustomMethod(methodInfo, parameters, convertParameters, context); } catch { @@ -41,7 +41,7 @@ internal static object Caller(Assembly assembly, string myclass, string mymet return GetDefaultValue(methodInfo.ReturnType); } - internal static object InvokeCustomMethod(MethodInfo methodInfo, object[] parameters, bool convertParameters, EvaluationMode evaluationMode) where T : ISelectableToken + internal static object InvokeCustomMethod(MethodInfo methodInfo, object[] parameters, bool convertParameters, IContext context) where T : ISelectableToken { var instance = !methodInfo.IsStatic ? Activator.CreateInstance(methodInfo.DeclaringType) : null; @@ -52,7 +52,7 @@ internal static object InvokeCustomMethod(MethodInfo methodInfo, object[] par for (int i = 0; i < parameterInfos.Length; i++) { var pType = parameterInfos[i].ParameterType; - typedParameters.Add(GetTypedValue(pType, parameters[i], evaluationMode)); + typedParameters.Add(GetTypedValue(pType, parameters[i], context.IsStrictMode())); } } try @@ -201,12 +201,12 @@ internal static Type GetType(JTokenType jType) return result; } - internal static object GetTypedValue(JTokenType jType, object val, EvaluationMode mode) + internal static object GetTypedValue(JTokenType jType, object val, bool IsStrictMode) { - return GetTypedValue(GetType(jType), val, mode); + return GetTypedValue(GetType(jType), val, IsStrictMode); } - internal static object GetTypedValue(Type pType, object val, EvaluationMode mode) + internal static object GetTypedValue(Type pType, object val, bool isStrictMode) { object typedValue = val; var converter = TypeDescriptor.GetConverter(pType); @@ -254,7 +254,7 @@ internal static object GetTypedValue(Type pType, object val, EvaluationMode mode } catch { - if (mode == EvaluationMode.Strict) + if (isStrictMode) { throw; } diff --git a/JUST.net/State.cs b/JUST.net/State.cs index ebeea91..56eb51f 100644 --- a/JUST.net/State.cs +++ b/JUST.net/State.cs @@ -10,13 +10,15 @@ internal struct LevelKey internal sealed class State { - internal State(JToken transformer, JToken input, int levelCounter) + internal State(JToken transformer, JToken input) { Transformer = transformer; ParentArray = new Dictionary(); - CurrentArrayToken = new Dictionary { { new LevelKey { Level = levelCounter, Key = "root"}, input } }; - CurrentScopeToken = new Dictionary { { new LevelKey { Level = levelCounter, Key = "root"}, input } }; + CurrentArrayToken = new Dictionary { { new LevelKey { Level = LevelCounter++, Key = "root"}, input } }; + CurrentScopeToken = new Dictionary { { new LevelKey { Level = LevelCounter++, Key = "root"}, input } }; } + + internal int LevelCounter = 0; internal JToken Transformer { get; private set; } internal IDictionary ParentArray { get; private set; } internal IDictionary CurrentArrayToken { get; private set; } @@ -50,4 +52,9 @@ private bool IsArrayHigherThanScope() { return (CurrentArrayToken?.Max(k => k.Key.Level) ?? 0) > (CurrentScopeToken?.Max(k => k.Key.Level) ?? 0); } + + internal JToken GetInput() + { + return CurrentScopeToken.Single(s => s.Key.Key == "root").Value; + } } \ No newline at end of file diff --git a/JUST.net/TransformHelper.cs b/JUST.net/TransformHelper.cs new file mode 100644 index 0000000..6a45c4f --- /dev/null +++ b/JUST.net/TransformHelper.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +internal class TransformHelper +{ + internal IList selectedTokens; + internal IDictionary tokensToReplace; + internal IList tokensToDelete; + internal IList loopProperties; + internal IList scopeProperties; + internal IList condProps; + internal JArray arrayToForm; + internal JObject dictToForm; + internal JObject scopeToForm; + internal IList tokenToForm; + internal IList tokensToAdd; + internal bool isLoop; + internal bool isBulk; + internal bool isScope; +} \ No newline at end of file diff --git a/JUST.net/Transformer.cs b/JUST.net/Transformer.cs index 010141e..47fc190 100644 --- a/JUST.net/Transformer.cs +++ b/JUST.net/Transformer.cs @@ -8,8 +8,6 @@ namespace JUST { public abstract class Transformer { - protected int _levelCounter = 0; - protected readonly JUSTContext Context; public Transformer(JUSTContext context) @@ -22,6 +20,13 @@ protected static object TypedNumber(decimal number) return number * 10 % 10 == 0 ? (number <= int.MaxValue ? (object)Convert.ToInt32(number) : number) : number; } + + internal static JToken GetToken(JToken input, string path, IContext context) where T : ISelectableToken + { + T selector = context.Resolve(input); + return selector.Select(path); + } + internal static object GetValue(JToken selectedToken) { object output = null; @@ -84,31 +89,28 @@ public Transformer(JUSTContext context) : base(context) { } - public static object valueof(string path, JUSTContext context) + public static object valueof(string path, JToken input, IContext context) { - T selector = context.Resolve(context.Input); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(input, path, context); return GetValue(selectedToken); } - public static bool exists(string path, JUSTContext context) + public static bool exists(string path, JToken input, IContext context) { - T selector = context.Resolve(context.Input); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(input, path, context); return selectedToken != null; } - public static bool existsandnotempty(string path, JUSTContext context) + public static bool existsandnotempty(string path, JToken input, IContext context) { - T selector = context.Resolve(context.Input); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(input, path, context); return selectedToken != null && ( (selectedToken.Type == JTokenType.String && selectedToken.ToString().Trim() != string.Empty) || (selectedToken.Type == JTokenType.Array && selectedToken.Children().Count() > 0) ); } - public static object ifcondition(object condition, object value, object trueResult, object falseResult, JUSTContext context) + public static object ifcondition(object condition, object value, object trueResult, object falseResult, JToken input, IContext context) { object output = falseResult; @@ -152,7 +154,7 @@ private static object ConcatArray(object obj1, object obj2) return item.ToObject(); } - public static object concat(object obj1, object obj2, JUSTContext context) + public static object concat(object obj1, object obj2, JToken input, IContext context) { if (obj1 != null) { @@ -174,7 +176,7 @@ public static object concat(object obj1, object obj2, JUSTContext context) return null; } - public static string substring(string stringRef, int startIndex, int length, JUSTContext context) + public static string substring(string stringRef, int startIndex, int length, JToken input, IContext context) { try { @@ -189,22 +191,22 @@ public static string substring(string stringRef, int startIndex, int length, JUS return null; } - public static int firstindexof(string stringRef, string searchString, JUSTContext context) + public static int firstindexof(string stringRef, string searchString, JToken input, IContext context) { return stringRef.IndexOf(searchString, 0); } - public static int lastindexof(string stringRef, string searchString, JUSTContext context) + public static int lastindexof(string stringRef, string searchString, JToken input, IContext context) { return stringRef.LastIndexOf(searchString); } - public static string concatall(object obj, JUSTContext context) + public static string concatall(object obj, JToken input, IContext context) { JToken token = JToken.FromObject(obj); if (obj is string path && path.StartsWith(context.Resolve(token).RootReference)) { - return Concatall(JToken.FromObject(valueof(path, context)), context); + return Concatall(JToken.FromObject(valueof(path, input, context)), context); } else { @@ -212,7 +214,7 @@ public static string concatall(object obj, JUSTContext context) } } - private static string Concatall(JToken parsedArray, JUSTContext context) + private static string Concatall(JToken parsedArray, IContext context) { string result = null; @@ -235,7 +237,7 @@ private static string Concatall(JToken parsedArray, JUSTContext context) return result; } - public static string concatallatpath(JArray parsedArray, string path, JUSTContext context) + public static string concatallatpath(JArray parsedArray, string path, JToken input, IContext context) { string result = null; @@ -244,8 +246,7 @@ public static string concatallatpath(JArray parsedArray, string path, JUSTContex result = string.Empty; foreach (JToken token in parsedArray.Children()) { - var selector = context.Resolve(token); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(token, path, context); if (context.IsStrictMode() && selectedToken.Type != JTokenType.String) { throw new Exception($"Invalid value in array to concatenate: {selectedToken.ToString()}"); @@ -262,40 +263,40 @@ public static string concatallatpath(JArray parsedArray, string path, JUSTContex #region math functions - public static object add(decimal num1, decimal num2, JUSTContext context) + public static object add(decimal num1, decimal num2, JToken input, IContext context) { return TypedNumber(num1 + num2); } - public static object subtract(decimal num1, decimal num2, JUSTContext context) + public static object subtract(decimal num1, decimal num2, JToken input, IContext context) { return TypedNumber(num1 - num2); } - public static object multiply(decimal num1, decimal num2, JUSTContext context) + public static object multiply(decimal num1, decimal num2, JToken input, IContext context) { return TypedNumber(num1 * num2); } - public static object divide(decimal num1, decimal num2, JUSTContext context) + public static object divide(decimal num1, decimal num2, JToken input, IContext context) { return TypedNumber(num1 / num2); } #endregion #region aggregate functions - public static object sum(object obj, JUSTContext context) + public static object sum(object obj, JToken input, IContext context) { JToken token = JToken.FromObject(obj); if (obj is string path && path.StartsWith(context.Resolve(token).RootReference)) { - return Sum(JToken.FromObject(valueof(path, context)), context); + return Sum(JToken.FromObject(valueof(path, input, context))); } else { - return Sum(token, context); + return Sum(token); } } - private static object Sum(JToken parsedArray, JUSTContext context) + private static object Sum(JToken parsedArray) { decimal result = 0; if (parsedArray != null) @@ -309,15 +310,15 @@ private static object Sum(JToken parsedArray, JUSTContext context) return TypedNumber(result); } - public static object sumatpath(JArray parsedArray, string path, JUSTContext context) + public static object sumatpath(JArray parsedArray, string path, JToken input, IContext context) { decimal result = 0; if (parsedArray != null) { foreach (JToken token in parsedArray.Children()) { - T selector = context.Resolve(token); - if (selector.Select(path) is JToken selectedToken) + JToken selectedToken = GetToken(token, path, context); + if (selectedToken != null) { result += Convert.ToDecimal(selectedToken.ToString()); } @@ -327,20 +328,20 @@ public static object sumatpath(JArray parsedArray, string path, JUSTContext cont return TypedNumber(result); } - public static object average(object obj, JUSTContext context) + public static object average(object obj, JToken input, IContext context) { JToken token = JToken.FromObject(obj); if (obj is string path && path.StartsWith(context.Resolve(token).RootReference)) { - return Average(JToken.FromObject(valueof(path, context)), context); + return Average(JToken.FromObject(valueof(path, input, context))); } else { - return Average(token, context); + return Average(token); } } - private static object Average(JToken token, JUSTContext context) + private static object Average(JToken token) { decimal result = 0; JArray parsedArray = token as JArray; @@ -355,7 +356,7 @@ private static object Average(JToken token, JUSTContext context) return TypedNumber(result / parsedArray.Count); } - public static object averageatpath(JArray parsedArray, string path, JUSTContext context) + public static object averageatpath(JArray parsedArray, string path, JToken input, IContext context) { decimal result = 0; @@ -363,8 +364,8 @@ public static object averageatpath(JArray parsedArray, string path, JUSTContext { foreach (JToken token in parsedArray.Children()) { - T selector = context.Resolve(token); - if (selector.Select(path) is JToken selectedToken) + JToken selectedToken = GetToken(token, path, context); + if (selectedToken != null) { result += Convert.ToDecimal(selectedToken.ToString()); } @@ -374,20 +375,20 @@ public static object averageatpath(JArray parsedArray, string path, JUSTContext return TypedNumber(result / parsedArray.Count); } - public static object max(object obj, JUSTContext context) + public static object max(object obj, JToken input, IContext context) { JToken token = JToken.FromObject(obj); if (obj is string path && path.StartsWith(context.Resolve(token).RootReference)) { - return Max(JToken.FromObject(valueof(path, context)), context); + return Max(JToken.FromObject(valueof(path, input, context))); } else { - return Max(token, context); + return Max(token); } } - private static object Max(JToken token, JUSTContext context) + private static object Max(JToken token) { decimal result = 0; if (token != null) @@ -407,15 +408,14 @@ private static decimal Max(decimal d1, JToken token) return Math.Max(d1, thisValue); } - public static object maxatpath(JArray parsedArray, string path, JUSTContext context) + public static object maxatpath(JArray parsedArray, string path, JToken input, IContext context) { decimal result = 0; if (parsedArray != null) { foreach (JToken token in parsedArray.Children()) { - T selector = context.Resolve(token); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(token, path, context); result = Max(result, selectedToken); } } @@ -423,20 +423,20 @@ public static object maxatpath(JArray parsedArray, string path, JUSTContext cont return TypedNumber(result); } - public static object min(object obj, JUSTContext context) + public static object min(object obj, JToken input, IContext context) { JToken token = JToken.FromObject(obj); if (obj is string path && path.StartsWith(context.Resolve(token).RootReference)) { - return Min(JToken.FromObject(valueof(path, context)), context); + return Min(JToken.FromObject(valueof(path, input, context))); } else { - return Min(token, context); + return Min(token); } } - private static object Min(JToken token, JUSTContext context) + private static object Min(JToken token) { decimal result = decimal.MaxValue; if (token != null) @@ -451,7 +451,7 @@ private static object Min(JToken token, JUSTContext context) return TypedNumber(result); } - public static object minatpath(JArray parsedArray, string path, JUSTContext context) + public static object minatpath(JArray parsedArray, string path, JToken input, IContext context) { decimal? result = null; @@ -459,8 +459,8 @@ public static object minatpath(JArray parsedArray, string path, JUSTContext cont { foreach (JToken token in parsedArray.Children()) { - T selector = context.Resolve(token); - if (selector.Select(path) is JToken selectedToken) + JToken selectedToken = GetToken(token, path, context); + if (selectedToken != null) { decimal thisValue = Convert.ToDecimal(selectedToken.ToString()); result = Math.Min(result ?? decimal.MaxValue, thisValue); @@ -471,7 +471,7 @@ public static object minatpath(JArray parsedArray, string path, JUSTContext cont return TypedNumber(result ?? decimal.MaxValue); } - public static int arraylength(string array, JUSTContext context) + public static int arraylength(string array, JToken input, IContext context) { JArray parsedArray = JArray.Parse(array); return parsedArray.Count; @@ -500,14 +500,13 @@ public static int lastindex(JArray array, JToken currentElement) return array.Count - 1; } - public static object currentvalueatpath(JArray array, JToken currentElement, string path, JUSTContext context) + public static object currentvalueatpath(JArray array, JToken currentElement, string path, IContext context) { - var selector = context.Resolve(currentElement); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(currentElement, path, context); return GetValue(selectedToken); } - public static object currentproperty(JArray array, JToken currentElement, JUSTContext context) + public static object currentproperty(JArray array, JToken currentElement, IContext context) { var prop = (currentElement.First as JProperty); if (prop == null && context.IsStrictMode()) @@ -517,22 +516,21 @@ public static object currentproperty(JArray array, JToken currentElement, JUSTCo return prop.Name; } - public static object lastvalueatpath(JArray array, JToken currentElement, string path, JUSTContext context) + public static object lastvalueatpath(JArray array, JToken currentElement, string path, IContext context) { - T selector = context.Resolve(array.Last); - JToken selectedToken = selector.Select(path); + JToken selectedToken = GetToken(array.Last(), path, context); return GetValue(selectedToken); } #endregion #region Constants - public static string constant_comma(JUSTContext context) + public static string constant_comma(JToken input, IContext context) { return ","; } - public static string constant_hash(JUSTContext context) + public static string constant_hash(JToken input, IContext context) { return "#"; } @@ -548,7 +546,7 @@ public static object xconcat(object[] list) { if (list[i] != null) { - result = concat(result, list[i], null); + result = concat(result, list[i], null, null); } } @@ -557,12 +555,12 @@ public static object xconcat(object[] list) public static object xadd(object[] list) { - JUSTContext context = list[list.Length - 1] as JUSTContext; + IContext context = list[list.Length - 1] as IContext; decimal add = 0; for (int i = 0; i < list.Length - 1; i++) { if (list[i] != null) - add += (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[i], context.EvaluationMode); + add += (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[i], context.IsStrictMode()); } return TypedNumber(add); @@ -570,11 +568,10 @@ public static object xadd(object[] list) #endregion #region grouparrayby - public static JArray grouparrayby(string path, string groupingElement, string groupedElement, JUSTContext context) + public static JArray grouparrayby(string path, string groupingElement, string groupedElement, JToken input, IContext context) { JArray result; - T selector = context.Resolve(context.Input); - JArray arr = (JArray)selector.Select(path); + JArray arr = (JArray)GetToken(input, path, context); if (!groupingElement.Contains(context.SplitGroupChar)) { result = Utilities.GroupArray(arr, groupingElement, groupedElement, context); @@ -631,10 +628,10 @@ public static bool mathequals(object[] list) { decimal lshDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[0], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); decimal rhsDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[1], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); result = lshDecimal == rhsDecimal; } @@ -649,10 +646,10 @@ public static bool mathgreaterthan(object[] list) { decimal lshDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[0], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); decimal rhsDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[1], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); result = lshDecimal > rhsDecimal; } @@ -667,10 +664,10 @@ public static bool mathlessthan(object[] list) { decimal lshDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[0], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); decimal rhsDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[1], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); result = lshDecimal < rhsDecimal; } @@ -685,10 +682,10 @@ public static bool mathgreaterthanorequalto(object[] list) { decimal lshDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[0], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); decimal rhsDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[1], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); result = lshDecimal >= rhsDecimal; } @@ -703,10 +700,10 @@ public static bool mathlessthanorequalto(object[] list) { decimal lshDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[0], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); decimal rhsDecimal = (decimal)ReflectionHelper.GetTypedValue(typeof(decimal), list[1], - list.Length >= 3 ? ((JUSTContext)list[2]).EvaluationMode : EvaluationMode.Strict); + list.Length >= 3 ? ((IContext)list[2]).IsStrictMode() : true); result = lshDecimal <= rhsDecimal; } @@ -715,37 +712,37 @@ public static bool mathlessthanorequalto(object[] list) } #endregion - public static object tointeger(object val, JUSTContext context) + public static object tointeger(object val, JToken input, IContext context) { - return ReflectionHelper.GetTypedValue(typeof(int), val, context.EvaluationMode); + return ReflectionHelper.GetTypedValue(typeof(int), val, context.IsStrictMode()); } - public static object tostring(object val, JUSTContext context) + public static object tostring(object val, JToken input, IContext context) { - return ReflectionHelper.GetTypedValue(typeof(string), val, context.EvaluationMode); + return ReflectionHelper.GetTypedValue(typeof(string), val, context.IsStrictMode()); } - public static object toboolean(object val, JUSTContext context) + public static object toboolean(object val, JToken input, IContext context) { - return ReflectionHelper.GetTypedValue(typeof(bool), val, context.EvaluationMode); + return ReflectionHelper.GetTypedValue(typeof(bool), val, context.IsStrictMode()); } - public static decimal todecimal(object val, JUSTContext context) + public static decimal todecimal(object val, JToken input, IContext context) { - return decimal.Round((decimal)ReflectionHelper.GetTypedValue(typeof(decimal), val, context.EvaluationMode), context.DefaultDecimalPlaces); + return decimal.Round((decimal)ReflectionHelper.GetTypedValue(typeof(decimal), val, context.IsStrictMode()), context.DefaultDecimalPlaces); } - public static decimal round(decimal val, int decimalPlaces, JUSTContext context) + public static decimal round(decimal val, int decimalPlaces, JToken input, IContext context) { return decimal.Round(val, decimalPlaces, MidpointRounding.AwayFromZero); } - public static int length(object val, JUSTContext context) + public static int length(object val, JToken input, IContext context) { int result = 0; if (val is string path && path.StartsWith(context.Resolve(null).RootReference)) { - result = length(valueof(path, context), context); + result = length(valueof(path, input, context), input, context); } else if (val is IEnumerable enumerable) { @@ -764,11 +761,11 @@ public static int length(object val, JUSTContext context) } return result; } - public static bool isnumber(object val, JUSTContext context) + public static bool isnumber(object val, JToken input, IContext context) { try { - object r = ReflectionHelper.GetTypedValue(typeof(decimal), val, context.EvaluationMode); + object r = ReflectionHelper.GetTypedValue(typeof(decimal), val, context.IsStrictMode()); return r is decimal; } catch @@ -777,17 +774,17 @@ public static bool isnumber(object val, JUSTContext context) } } - public static bool isboolean(object val, JUSTContext context) + public static bool isboolean(object val, JToken input, IContext context) { return val != null ? val.GetType() == typeof(bool) : false; } - public static bool isstring(object val, JUSTContext context) + public static bool isstring(object val, JToken input, IContext context) { return val != null ? val.GetType() == typeof(string) : false; } - public static bool isarray(object val, JUSTContext context) + public static bool isarray(object val, JToken input, IContext context) { return val != null ? val.GetType().IsArray : false; } @@ -802,7 +799,7 @@ public static string stringempty() return string.Empty; } - public static object arrayempty(JUSTContext context) + public static object arrayempty(JToken input, IContext context) { return Array.Empty(); } diff --git a/JUST.net/Utilities.cs b/JUST.net/Utilities.cs index fb688e7..ee1aa8c 100644 --- a/JUST.net/Utilities.cs +++ b/JUST.net/Utilities.cs @@ -45,7 +45,7 @@ private static Dictionary PopulateRecursively(JToken parent, Dic return result; } - public static JArray GroupArray(JArray array, string groupingPropertyName, string groupedPropertyName, JUSTContext context) where T: ISelectableToken + public static JArray GroupArray(JArray array, string groupingPropertyName, string groupedPropertyName, IContext context) where T: ISelectableToken { Dictionary groupedPair = null; @@ -55,7 +55,6 @@ public static JArray GroupArray(JArray array, string groupingPropertyName, st { var selectable = context.Resolve(eachObj); JToken groupToken = selectable.Select(selectable.RootReference + groupingPropertyName); - if (groupedPair == null) groupedPair = new Dictionary(); @@ -105,7 +104,7 @@ public static JArray GroupArray(JArray array, string groupingPropertyName, st return resultObj; } - public static JArray GroupArrayMultipleProperties(JArray array, string[] groupingPropertyNames, string groupedPropertyName, JUSTContext context) where T: ISelectableToken + public static JArray GroupArrayMultipleProperties(JArray array, string[] groupingPropertyNames, string groupedPropertyName, IContext context) where T: ISelectableToken { Dictionary groupedPair = null; diff --git a/UnitTests/Arrays/AggregateFunctionsTests.cs b/UnitTests/Arrays/AggregateFunctionsTests.cs index 292824c..0daa64a 100644 --- a/UnitTests/Arrays/AggregateFunctionsTests.cs +++ b/UnitTests/Arrays/AggregateFunctionsTests.cs @@ -113,7 +113,7 @@ public void AverageAtPath() { const string transformer = "{ \"avg_at_path\": \"#averageatpath(#valueof($.x),$.v.c)\" }"; - var result = new JsonTransformer().Transform(transformer, ExampleInputs.MultiDimensionalArray); + var result = new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }).Transform(transformer, ExampleInputs.MultiDimensionalArray); Assert.AreEqual("{\"avg_at_path\":20}", result); } diff --git a/UnitTests/Arrays/LoopingTests.cs b/UnitTests/Arrays/LoopingTests.cs index 65ec48e..ecb5c91 100644 --- a/UnitTests/Arrays/LoopingTests.cs +++ b/UnitTests/Arrays/LoopingTests.cs @@ -140,7 +140,7 @@ public void NestedLoopingContextInput() { const string transformer = "{ \"hello\": { \"#loop($.NestedLoop.Organization.Employee)\": { \"Details\": { \"#loop($.Details)\": { \"Exists\": \"#exists($.Country)\", \"IsIsland\": \"#ifcondition(#currentvalueatpath($.Country),Iceland,#toboolean(True),#toboolean(False))\", \"CurrentCountry\": \"#currentvalueatpath($.Country)\" } } } } }"; - var result = new JsonTransformer().Transform(transformer, ExampleInputs.NestedArrays); + var result = new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }).Transform(transformer, ExampleInputs.NestedArrays); Assert.AreEqual("{\"hello\":[{\"Details\":[{\"Exists\":true,\"IsIsland\":true,\"CurrentCountry\":\"Iceland\"}]},{\"Details\":[{\"Exists\":true,\"IsIsland\":false,\"CurrentCountry\":\"Denmark\"}]}]}", result); } @@ -262,7 +262,7 @@ public void MixedLoopingAlias() { const string transformer = "{ \"hello\": { \"#loop($.NestedLoop.Organization.Employee, employee)\": { \"Details\": { \"#loop($.Details)\": { \"CurrentCountry\": \"#currentvalueatpath($.Country)\", \"OuterName\": \"#currentvalueatpath($.Name, employee)\" } } } } }"; - var result = new JsonTransformer().Transform(transformer, ExampleInputs.NestedArrays); + var result = new JsonTransformer(new JUSTContext() { EvaluationMode = EvaluationMode.Strict }).Transform(transformer, ExampleInputs.NestedArrays); Assert.AreEqual("{\"hello\":[{\"Details\":[{\"CurrentCountry\":\"Iceland\",\"OuterName\":\"E2\"}]},{\"Details\":[{\"CurrentCountry\":\"Denmark\",\"OuterName\":\"E1\"}]}]}", result); } diff --git a/UnitTests/CustomFunctionsTest.cs b/UnitTests/CustomFunctionsTest.cs index a98a2ab..a953bf4 100644 --- a/UnitTests/CustomFunctionsTest.cs +++ b/UnitTests/CustomFunctionsTest.cs @@ -14,7 +14,7 @@ public void XmlTest() string inputSpecial = File.ReadAllText("Inputs/Input_Customfunction_Nestedresult.json"); string transformer = File.ReadAllText("Inputs/Transformer_customfunctionnestedresult.json"); string result = JsonConvert.SerializeObject - (new JsonTransformer().Transform(JObject.Parse(transformer), JObject.Parse(inputSpecial))); + (new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }).Transform(JObject.Parse(transformer), JObject.Parse(inputSpecial))); Assert.AreEqual("{\"Seasons\":[[[[\"2017\"],[\"40\"],[\"20\"],[\"25\"],[\"10\"]],[[\"2018\"],[\"40\"],[\"20\"],[\"25\"],[\"10\"]],[[\"2019\"],[\"40\"],[\"20\"],[\"25\"],[\"10\"]]]]}", result); } diff --git a/UnitTests/Inputs/thread_safe_input.json b/UnitTests/Inputs/thread_safe_input.json new file mode 100644 index 0000000..00e3f4a --- /dev/null +++ b/UnitTests/Inputs/thread_safe_input.json @@ -0,0 +1,88 @@ +{ + "list": [ + { + "title": "Mr.", + "name": "Smith", + "addresses": [ + { + "street": "Some Street", + "number": 1, + "city": "Some City", + "postal_code": 1234 + }, + { + "street": "Some Other Street", + "number": 2, + "city": "Some Other City", + "postal_code": 5678 + } + ], + "contacts": [ + { + "type": "home", + "number": 123546789, + "is_default": false + }, + { + "type": "mobile", + "number": 987654321, + "is_default": true + } + ] + }, + { + "title": "Mrs.", + "name": "Smith", + "addresses": [ + { + "street": "Street Who?", + "number": 11, + "city": "City Who?", + "postal_code": 1111 + }, + { + "street": "Other Street Who?", + "number": 22, + "city": "Other City Who?", + "postal_code": 2222 + } + ], + "contacts": [ + { + "type": "home", + "number": 999999999999999, + "is_default": false + }, + { + "type": "mobile", + "number": 111111111111111, + "is_default": true + } + ] + } + ], + "title": "Mr.", + "name": "Smith", + "addresses": [{ + "street": "Some Street", + "number": 1, + "city": "Some City", + "postal_code": 1234 + }, { + "street": "Some Other Street", + "number": 2, + "city": "Some Other City", + "postal_code": 5678 + } + ], + "contacts": [{ + "type": "home", + "number": 123546789, + "is_default": false + }, { + "type": "mobile", + "number": 987654321, + "is_default": true + } + ] +} \ No newline at end of file diff --git a/UnitTests/JUST.net.UnitTests.csproj b/UnitTests/JUST.net.UnitTests.csproj index fbc1c9b..6bc8e29 100644 --- a/UnitTests/JUST.net.UnitTests.csproj +++ b/UnitTests/JUST.net.UnitTests.csproj @@ -32,6 +32,9 @@ Always + + Always + \ No newline at end of file diff --git a/UnitTests/LoadTests.cs b/UnitTests/LoadTests.cs index 26e1488..96ab7e1 100644 --- a/UnitTests/LoadTests.cs +++ b/UnitTests/LoadTests.cs @@ -15,7 +15,7 @@ public void LargeInput() const string transformer = "{ \"result\": { \"#loop($.list)\": { \"id\": \"#currentindex()\", \"name\": \"#concat(#currentvalueatpath($.title), #currentvalueatpath($.name))\", \"contact\": \"#currentvalueatpath($.contacts[?(@.is_default==true)])\", \"address\": \"#currentvalueatpath($.addresses[0])\" } }"; var w = Stopwatch.StartNew(); - new JsonTransformer().Transform(transformer, input); + new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }).Transform(transformer, input); w.Stop(); var timeConsumed = w.Elapsed; Assert.LessOrEqual(timeConsumed, TimeSpan.FromSeconds(5)); @@ -24,11 +24,11 @@ public void LargeInput() [Test] public void LargeTransformer() { - const string input = "{ \" title\" : \" Mr.\" , \" name\" : \" Smith\" , \" addresses\" : [ { \" street\" : \" Some Street\" , \" number\" : 1, \" city\" : \" Some City\" , \" postal_code\" : 1234 }, { \" street\" : \" Some Other Street\" , \" number\" : 2, \" city\" : \" Some Other City\" , \" postal_code\" : 5678 } ], \" contacts\" : [ { \" type\" : \" home\" , \" number\" : 123546789, \" is_default\" : false }, { \" type\" : \" mobile\" , \" number\" : 987654321, \" is_default\" : true } ] }"; + const string input = "{ \"title\": \"Mr.\", \"name\": \"Smith\", \"addresses\": [ { \"street\": \"Some Street\", \"number\": 1, \"city\": \"Some City\", \"postal_code\": 1234 }, { \"street\": \"Some Other Street\", \"number\": 2, \"city\": \"Some Other City\", \"postal_code\": 5678 } ], \"contacts\": [ { \"type\": \"home\", \"number\": 123546789, \"is_default\": false }, { \"type\": \"mobile\", \"number\": 987654321, \"is_default\": true } ] }"; var transformer = File.ReadAllText("Inputs/large_transformer.json"); var w = Stopwatch.StartNew(); - new JsonTransformer().Transform(transformer, input); + var e = new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }).Transform(transformer, input); w.Stop(); var timeConsumed = w.Elapsed; Assert.LessOrEqual(timeConsumed, TimeSpan.FromSeconds(5)); diff --git a/UnitTests/ThreadSafeTests.cs b/UnitTests/ThreadSafeTests.cs new file mode 100644 index 0000000..09053f2 --- /dev/null +++ b/UnitTests/ThreadSafeTests.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using System.IO; +using System.Threading.Tasks; + +namespace JUST.UnitTests +{ + [TestFixture] + public class ThreadSafeTests + { + [Test] + public void TestTransforms1() + { + var input = File.ReadAllText("Inputs/thread_safe_input.json"); + const string transformer = "{ \"list\": { \"#loop($.list)\": { \"id\": \"#currentindex()\", \"name\": \"#concat(#currentvalueatpath($.title), #currentvalueatpath($.name))\", \"contact\": \"#currentvalueatpath($.contacts[?(@.is_default==true)])\", \"address\": \"#currentvalueatpath($.addresses[0])\" } }, \"applyover\": \"#applyover({ 'first_title': '#valueof($.list[0].title)'/, 'second_address': '#valueof($.list[1].addresses[1])'/, 'first_contact': '#valueof($.list[0].contacts[0])' }, '#valueof($.second_address.street)')\" }"; + const string expect = "{\"list\":[{\"id\":0,\"name\":\"Mr.Smith\",\"contact\":{\"type\":\"mobile\",\"number\":987654321,\"is_default\":true},\"address\":{\"street\":\"Some Street\",\"number\":1,\"city\":\"Some City\",\"postal_code\":1234}},{\"id\":1,\"name\":\"Mrs.Smith\",\"contact\":{\"type\":\"mobile\",\"number\":111111111111111,\"is_default\":true},\"address\":{\"street\":\"Street Who?\",\"number\":11,\"city\":\"City Who?\",\"postal_code\":1111}}],\"applyover\":\"Other Street Who?\"}"; + + JsonTransformer jsonTransformer = new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }); + + string transformResult = jsonTransformer.Transform(transformer, input); + Assert.AreEqual(expect, transformResult); + } + + [Test] + public void TestTransforms2() + { + var input = File.ReadAllText("Inputs/thread_safe_input.json"); + const string transformer = "{ \"result\": { \"name\": \"#concat(#valueof($.title), #valueof($.name))\", \"contact\": \"#valueof($.contacts[?(@.is_default==true)])\", \"address\": \"#valueof($.addresses[0])\" } }"; + const string expect = "{\"result\":{\"name\":\"Mr.Smith\",\"contact\":{\"type\":\"mobile\",\"number\":987654321,\"is_default\":true},\"address\":{\"street\":\"Some Street\",\"number\":1,\"city\":\"Some City\",\"postal_code\":1234}}}"; + + JsonTransformer jsonTransformer = new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }); + + string transformResult = jsonTransformer.Transform(transformer, input); + Assert.AreEqual(expect, transformResult); + } + + [Test] + public void ThreadSafe() + { + string threadSafeInput = File.ReadAllText("Inputs/thread_safe_input.json"); + string[] inputs = + { + threadSafeInput, + threadSafeInput, + ExampleInputs.Menu, + + }; + string[] transformers = + { + "{ \"list\": { \"#loop($.list)\": { \"id\": \"#currentindex()\", \"name\": \"#concat(#currentvalueatpath($.title), #currentvalueatpath($.name))\", \"contact\": \"#currentvalueatpath($.contacts[?(@.is_default==true)])\", \"address\": \"#currentvalueatpath($.addresses[0])\" } }, \"applyover\": \"#applyover({ 'first_title': '#valueof($.list[0].title)'/, 'second_address': '#valueof($.list[1].addresses[1])'/, 'first_contact': '#valueof($.list[0].contacts[0])' }, '#valueof($.second_address.street)')\" }", + "{ \"result\": { \"name\": \"#concat(#valueof($.title), #valueof($.name))\", \"contact\": \"#valueof($.contacts[?(@.is_default==true)])\", \"address\": \"#valueof($.addresses[0])\" } }", + "{\"root\": {\"menu1\": \"#valueof($.menu.popup.menuitem[?(@.value=='New')].onclick)\", \"menu2\": \"#valueof($.menu.popup.menuitem[?(@.value=='Open')].onclick)\"}}", + }; + string[] expects = + { + "{\"list\":[{\"id\":0,\"name\":\"Mr.Smith\",\"contact\":{\"type\":\"mobile\",\"number\":987654321,\"is_default\":true},\"address\":{\"street\":\"Some Street\",\"number\":1,\"city\":\"Some City\",\"postal_code\":1234}},{\"id\":1,\"name\":\"Mrs.Smith\",\"contact\":{\"type\":\"mobile\",\"number\":111111111111111,\"is_default\":true},\"address\":{\"street\":\"Street Who?\",\"number\":11,\"city\":\"City Who?\",\"postal_code\":1111}}],\"applyover\":\"Other Street Who?\"}", + "{\"result\":{\"name\":\"Mr.Smith\",\"contact\":{\"type\":\"mobile\",\"number\":987654321,\"is_default\":true},\"address\":{\"street\":\"Some Street\",\"number\":1,\"city\":\"Some City\",\"postal_code\":1234}}}", + "{\"root\":{\"menu1\":{\"action\":\"CreateNewDoc()\"},\"menu2\":\"OpenDoc()\"}}" + }; + + var jsonTransformer = new JsonTransformer(new JUSTContext { EvaluationMode = EvaluationMode.Strict }); + + ParallelLoopResult result = Parallel.For(0, 100, i => + { + JToken transformResult = jsonTransformer.Transform(JObject.Parse(transformers[i % 3]), inputs[i % 3]); + Assert.AreEqual(JObject.Parse(expects[i % 3]), transformResult); + }); + + Assert.IsTrue(result.IsCompleted); + } + } +} diff --git a/UnitTests/ValueOfTests.cs b/UnitTests/ValueOfTests.cs index 4735b8a..6238103 100644 --- a/UnitTests/ValueOfTests.cs +++ b/UnitTests/ValueOfTests.cs @@ -11,7 +11,7 @@ public void String() const string input = "{ \"string\": \"some words\", \"integer\": 123, \"boolean\": true }"; const string transformer = "{ \"result\": \"#valueof($.string)\" }"; - var result = new JsonTransformer().Transform(transformer, input); + var result = new JsonTransformer(new JUSTContext() { EvaluationMode = EvaluationMode.Strict }).Transform(transformer, input); Assert.AreEqual("{\"result\":\"some words\"}", result); }