public JToken Transform(JToken input, JObject context) { var contextWrapper = new JObject(); contextWrapper.Add(ROOT_KEY, context); MatchedElement rootLpe = new MatchedElement(ROOT_KEY); WalkedPath walkedPath = new WalkedPath(); walkedPath.Add(input, rootLpe); _rootSpec.Apply(ROOT_KEY, input, walkedPath, null, contextWrapper); return(input); }
/** * * @return null if no work was done, otherwise returns the re-parented data */ private JToken PerformCardinalityAdjustment(string inputKey, JToken input, WalkedPath walkedPath, JObject parentContainer, MatchedElement thisLevel) { // Add our the LiteralPathElement for this level, so that write path References can use it as &(0,0) walkedPath.Add(input, thisLevel); JToken returnValue = null; if (_cardinalityRelationship == CardinalityRelationship.MANY) { if (input is JArray) { returnValue = input; } else if (input.Type == JTokenType.Object || input.Type == JTokenType.String || input.Type == JTokenType.Integer || input.Type == JTokenType.Float || input.Type == JTokenType.Boolean) { var one = parentContainer[inputKey]; parentContainer.Remove(inputKey); var tempList = new JArray(); tempList.Add(one); returnValue = tempList; } else if (input.Type == JTokenType.Null) { returnValue = new JArray(); } parentContainer[inputKey] = returnValue; } else if (_cardinalityRelationship == CardinalityRelationship.ONE) { if (input is JArray l) { // The value is first removed from the array in order to prevent it // from being cloned (JContainers clone their inputs if they are already // part of an object graph) if (l.Count > 0) { returnValue = l[0]; l.RemoveAt(0); } parentContainer[inputKey] = returnValue; } } walkedPath.RemoveLast(); return(returnValue); }
/** * Applies the Shiftr transform. * * @param input the JSON object to transform * @return the output object with data shifted to it * @throws com.bazaarvoice.jolt.exception.TransformException for a malformed spec or if there are issues during * the transform */ public JToken Transform(JToken input) { var output = new JObject(); // Create a root LiteralPathElement so that # is useful at the root level MatchedElement rootLpe = new MatchedElement(ROOT_KEY); WalkedPath walkedPath = new WalkedPath(); walkedPath.Add(input, rootLpe); _rootSpec.Apply(ROOT_KEY, input, walkedPath, output, null); output.TryGetValue(ROOT_KEY, out var result); return(result ?? JValue.CreateNull()); }
protected override void ApplyElement(string inputKey, JToken inputOptional, MatchedElement thisLevel, WalkedPath walkedPath, JObject context) { JToken input = inputOptional; // sanity checks, cannot work on a list spec with map input and vice versa, and runtime with null input if (!_specDataType.IsCompatible(input)) { return; } // create input if it is null if (input == null || input.Type == JTokenType.Null) { input = _specDataType.Create(inputKey, walkedPath, _opMode); // if input has changed, wrap if (input != null && input.Type != JTokenType.Null) { inputOptional = input; } } // if input is List, create special ArrayMatchedElement, which tracks the original size of the input array if (input is JArray list) { // LIST means spec had array index explicitly specified, hence expand if needed if (_specDataType is LIST specList) { int?origSize = specList.Expand(input); thisLevel = new ArrayMatchedElement(thisLevel.RawKey, origSize ?? 0); } else { // specDataType is RUNTIME, so spec had no array index explicitly specified, no need to expand thisLevel = new ArrayMatchedElement(thisLevel.RawKey, list.Count); } } // add self to walked path walkedPath.Add(input, thisLevel); // Handle the rest of the children _executionStrategy.Process(this, inputOptional, walkedPath, null, context); // We are done, so remove ourselves from the walkedPath walkedPath.RemoveLast(); }
/** * If this Spec matches the inputKey, then perform one step in the Shiftr parallel treewalk. * * Step one level down the input "tree" by carefully handling the List/Map nature the input to * get the "one level down" data. * * Step one level down the Spec tree by carefully and efficiently applying our children to the * "one level down" data. * * @return true if this this spec "handles" the inputKey such that no sibling specs need to see it */ public override bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context) { MatchedElement thisLevel = _pathElement.Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } // If we are a TransposePathElement, try to swap the "input" with what we lookup from the Transpose if (_pathElement is TransposePathElement tpe) { // Note the data found may not be a string, thus we have to call the special objectEvaluate // Optional, because the input data could have been a valid null. var optional = tpe.ObjectEvaluate(walkedPath); if (optional == null) { return(false); } inputOptional = optional; } // add ourselves to the path, so that our children can reference us walkedPath.Add(inputOptional, thisLevel); // Handle any special / key based children first, but don't have them block anything foreach (ShiftrSpec subSpec in _specialChildren) { subSpec.Apply(inputKey, inputOptional, walkedPath, output, context); } // Handle the rest of the children _executionStrategy.Process(this, inputOptional, walkedPath, output, context); // We are done, so remove ourselves from the walkedPath walkedPath.RemoveLast(); // we matched so increment the matchCount of our parent walkedPath.LastElement().MatchedElement.IncrementHashCount(); return(true); }
/** * If this Spec matches the inputkey, then perform one step in the parallel treewalk. * <p/> * Step one level down the input "tree" by carefully handling the List/Map nature the input to * get the "one level down" data. * <p/> * Step one level down the Spec tree by carefully and efficiently applying our children to the * "one level down" data. * * @return true if this this spec "handles" the inputkey such that no sibling specs need to see it */ public override bool ApplyCardinality(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer) { MatchedElement thisLevel = GetPathElement().Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } walkedPath.Add(input, thisLevel); // The specialChild can change the data object that I point to. // Aka, my key had a value that was a List, and that gets changed so that my key points to a ONE value if (_specialChild != null) { input = _specialChild.ApplyToParentContainer(inputKey, input, walkedPath, parentContainer); } // Handle the rest of the children Process(input, walkedPath); walkedPath.RemoveLast(); return(true); }
/** * If this Spec matches the inputkey, then do the work of outputting data and return true. * * @return true if this this spec "handles" the inputkey such that no sibling specs need to see it */ public override bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context) { JToken input = inputOptional; MatchedElement thisLevel = _pathElement.Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } JToken data; bool realChild = false; // by default don't block further Shiftr matches if (_pathElement is DollarPathElement || _pathElement is HashPathElement) { // The data is already encoded in the thisLevel object created by the pathElement.match called above data = thisLevel.GetCanonicalForm(); } else if (_pathElement is AtPathElement) { // The data is our parent's data data = input; } else if (_pathElement is TransposePathElement tpe) { // We try to walk down the tree to find the value / data we want // Note the data found may not be a string, thus we have to call the special objectEvaluate var evaledData = tpe.ObjectEvaluate(walkedPath); if (evaledData != null) { data = evaledData; } else { // if we could not find the value we want looking down the tree, bail return(false); } } else { // the data is the input data = input; // tell our parent that we matched and no further processing for this inputKey should be done realChild = true; } // Add our the LiteralPathElement for this level, so that write path References can use it as &(0,0) walkedPath.Add(input, thisLevel); // Write out the data foreach (PathEvaluatingTraversal outputPath in _shiftrWriters) { outputPath.Write(data, output, walkedPath); } walkedPath.RemoveLast(); if (realChild) { // we were a "real" child, so increment the matchCount of our parent walkedPath.LastElement().MatchedElement.IncrementHashCount(); } return(realChild); }
public void Apply(JToken input, WalkedPath walkedPath) { if (input is JArray arr) { HashSet <int> indexesToRemove = null; for (int i = 0; i < arr.Count; ++i) { var elt = arr[i]; var key = i.ToString(); foreach (var child in Children.Values) { var match = child.PathElement.Match(key, walkedPath); if (match == null) { continue; } if (IsFiltered(child, elt)) { if (indexesToRemove == null) { indexesToRemove = new HashSet <int>(); } indexesToRemove.Add(i); } else { walkedPath.Add(key, match); child.Apply(elt, walkedPath); walkedPath.RemoveLast(); } } } if (indexesToRemove != null) { foreach (var index in indexesToRemove.OrderByDescending(x => x)) { arr.RemoveAt(index); } } } else if (input is JObject obj) { HashSet <string> keysToRemove = null; foreach (var kv in obj) { foreach (var child in Children.Values) { var match = child.PathElement.Match(kv.Key, walkedPath); if (match == null) { continue; } if (IsFiltered(child, kv.Value)) { if (keysToRemove == null) { keysToRemove = new HashSet <string>(); } keysToRemove.Add(kv.Key); } else { walkedPath.Add(kv.Key, match); child.Apply(kv.Value, walkedPath); walkedPath.RemoveLast(); } } } if (keysToRemove != null) { foreach (string key in keysToRemove) { obj.Remove(key); } } } }