public PathStep(JToken treeRef, MatchedElement matchedElement) { TreeRef = treeRef; MatchedElement = matchedElement; if (MatchedElement is ArrayMatchedElement ame) { OrigSize = ame.GetOrigSize(); } }
/** * This should only be used by composite specs with an '@' child * * @return null if no work was done, otherwise returns the re-parented data */ public JToken ApplyToParentContainer(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer) { MatchedElement thisLevel = GetMatch(inputKey, walkedPath); if (thisLevel == null) { return(null); } return(PerformCardinalityAdjustment(inputKey, input, walkedPath, (JObject)parentContainer, thisLevel)); }
/** * If this CardinalitySpec matches the inputkey, then do the work of modifying the 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 ApplyCardinality(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer) { MatchedElement thisLevel = GetMatch(inputKey, walkedPath); if (thisLevel == null) { return(false); } PerformCardinalityAdjustment(inputKey, input, walkedPath, (JObject)parentContainer, thisLevel); return(true); }
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); }
/** * 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(); }
/** * Static utility method for facilitating writes on input object * * @param parent the source object * @param matchedElement the current spec (leaf) element that was matched with input * @param value to write * @param opMode to determine if write is applicable */ protected static void SetData(JToken parent, MatchedElement matchedElement, JToken value, OpMode opMode) { if (parent is JObject source) { string key = matchedElement.RawKey; if (opMode.IsApplicable(source, key)) { source[key] = value; } } else if (parent is JArray list && matchedElement is ArrayMatchedElement ame) { int origSize = ame.GetOrigSize(); int reqIndex = ame.GetRawIndex(); if (opMode.IsApplicable(list, reqIndex, origSize)) { list[reqIndex] = value; } }
/** * 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); }
public string Evaluate(WalkedPath walkedPath) { switch (_arrayPathType) { case ArrayPathType.AUTO_EXPAND: return(_canonicalForm); case ArrayPathType.EXPLICIT_INDEX: return(_arrayIndex); case ArrayPathType.HASH: MatchedElement element = walkedPath.ElementFromEnd(_ref.GetPathIndex()).MatchedElement; return(element.GetHashCount().ToString()); case ArrayPathType.TRANSPOSE: string key = _transposePathElement.Evaluate(walkedPath); return(VerifyStringIsNonNegativeInteger(key)); case ArrayPathType.REFERENCE: { MatchedElement lpe = walkedPath.ElementFromEnd(_ref.GetPathIndex()).MatchedElement; string keyPart; if (_ref is IPathAndGroupReference pagr) { keyPart = lpe.GetSubKeyRef(pagr.GetKeyGroup()); } else { keyPart = lpe.GetSubKeyRef(0); } return(VerifyStringIsNonNegativeInteger(keyPart)); } default: throw new InvalidOperationException("ArrayPathType enum added two without updating this switch statement."); } }
public string Evaluate(WalkedPath walkedPath) { // Walk thru our tokens and build up a string // Use the supplied Path to fill in our token References StringBuilder output = new StringBuilder(); foreach (object token in _tokens) { if (token is string stoken) { output.Append(stoken); } else { AmpReference ref_ = (AmpReference)token; MatchedElement matchedElement = walkedPath.ElementFromEnd(ref_.GetPathIndex()).MatchedElement; string value = matchedElement.GetSubKeyRef(ref_.GetKeyGroup()); output.Append(value); } } return(output.ToString()); }
/** * 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); }
public bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context) { if (output != null) { throw new TransformException("Expected a null output"); } MatchedElement thisLevel = _pathElement.Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } if (!_checkValue) // there was no trailing "?" so no check is necessary { ApplyElement(inputKey, inputOptional, thisLevel, walkedPath, context); } else if (inputOptional != null) { ApplyElement(inputKey, inputOptional, thisLevel, walkedPath, context); } 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); }
/** * * @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); }
/** * Templatr specific override that is used in BaseSpec#apply(...) * The name is changed for easy identification during debugging */ protected abstract void ApplyElement(string key, JToken inputOptional, MatchedElement thisLevel, WalkedPath walkedPath, JObject context);