private JsonPlusValue ResolveSubstitution(JsonPlusSubstitution sub) { JsonPlusObjectMember subField = sub.ParentMember; // first case, this substitution is a direct self-reference if (sub.Path == sub.GetMemberPath()) { IJsonPlusNode parent = sub.Parent; while (parent is JsonPlusValue) { parent = parent.Parent; } // Fail case if (parent is JsonPlusArray) { throw new JsonPlusException(RS.SelfRefSubstitutionInArray); } // try to resolve substitution by looking backward in the field assignment stack return(subField.OlderValueThan(sub)); } // need to recursively get full path JsonPlusPath fieldPath = subField.GetMemberPath(); // second case, the substitution references a field child in the past if (sub.Path.IsChildPathOf(fieldPath)) { JsonPlusValue olderValue = subField.OlderValueThan(sub); if ((olderValue != null) && (olderValue.Type == JsonPlusType.Object)) { int difLength = sub.Path.Count - fieldPath.Count; JsonPlusPath deltaPath = sub.Path.SubPath(sub.Path.Count - difLength, difLength); JsonPlusObject olderObject = olderValue.GetObject(); if (olderObject.TryGetValue(deltaPath, out JsonPlusValue innerValue)) { return(innerValue.Type == JsonPlusType.Object ? innerValue : null); } } } // Detect invalid parent-referencing substitution if (fieldPath.IsChildPathOf(sub.Path)) { throw new JsonPlusException(RS.SubstitutionRefDirectParentError); } // Detect invalid cyclic reference loop if (IsValueCyclic(subField, sub)) { throw new JsonPlusException(RS.CyclicSubstitutionLoop); } // third case, regular substitution _root.GetObject().TryGetValue(sub.Path, out JsonPlusValue field); return(field); }
internal JsonPlusValue OlderValueThan(IJsonPlusNode marker) { List <JsonPlusObject> objectList = new List <JsonPlusObject>(); int index = 0; while (index < _internalValues.Count) { JsonPlusValue value = _internalValues[index]; if (value.Any(v => ReferenceEquals(v, marker))) { break; } switch (value.Type) { case JsonPlusType.Object: objectList.Add(value.GetObject()); break; case JsonPlusType.Literal: case JsonPlusType.Array: objectList.Clear(); break; } index++; } if (objectList.Count == 0) { return(index == 0 ? null : _internalValues[index - 1]); } JsonPlusValue result = new JsonPlusValue(null); JsonPlusObject o = new JsonPlusObject(result); result.Add(o); foreach (JsonPlusObject obj in objectList) { o.Merge(obj); } return(result); }