/// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/> public IJsonPlusNode Clone(IJsonPlusNode newParent) { JsonPlusObjectMember newField = new JsonPlusObjectMember(Path, (JsonPlusObject)newParent); newField._internalValues.AddRange(_internalValues); return(newField); }
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); }
private JsonPlusObjectMember ParseObjectMember(JsonPlusObject owner) { // sanity check if (_tokens.Current.Type != TokenType.LiteralValue) { throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInMember, TokenType.LiteralValue, _tokens.Current.Type)); } JsonPlusPath pathDelta = ParseKey(); if (_tokens.Current.IsNonSignificant()) { ConsumeWhitelines(); } // sanity check if (_tokens.Current.Type != TokenType.Assignment && _tokens.Current.Type != TokenType.StartOfObject && _tokens.Current.Type != TokenType.SelfAssignment) { throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith3AltInMember, TokenType.Assignment, TokenType.StartOfObject, TokenType.SelfAssignment, _tokens.Current.Type)); } // sanity check if (pathDelta == null || pathDelta.Count == 0) { throw JsonPlusParserException.Create(_tokens.Current, Path, RS.ObjectMemberPathUnspecified); } List <JsonPlusObjectMember> childInPath = owner.TraversePath(pathDelta); Path.AddRange(pathDelta); JsonPlusObjectMember currentField = childInPath[childInPath.Count - 1]; JsonPlusValue parsedValue = ParseValue(currentField); foreach (JsonPlusSubstitution removedSub in currentField.SetValue(parsedValue)) { _substitutions.Remove(removedSub); } Path.RemoveRange(Path.Count - pathDelta.Count, pathDelta.Count); return(childInPath[0]); }
private JsonPlusPath GetMemberPath(JsonPlusObjectMember member, JsonPlusPath subPath) { if (member.ParentMember == null && subPath == null) { return(new JsonPlusPath(new string[] { member.Key })); } if (subPath == null) { return(GetMemberPath(member.ParentMember, new JsonPlusPath(new string[] { member.Key }))); } subPath.Add(member.Key); if (member.ParentMember == null) { return(subPath); } return(GetMemberPath(member.ParentMember, subPath)); }
private bool IsValueCyclic(JsonPlusObjectMember field, JsonPlusSubstitution sub) { Stack <JsonPlusValue> pendingValues = new Stack <JsonPlusValue>(); List <JsonPlusObjectMember> visitedFields = new List <JsonPlusObjectMember> { field }; Stack <JsonPlusSubstitution> pendingSubs = new Stack <JsonPlusSubstitution>(); pendingSubs.Push(sub); while (pendingSubs.Count > 0) { JsonPlusSubstitution currentSub = pendingSubs.Pop(); if (!_root.GetObject().TryGetMember(currentSub.Path, out var currentField)) { continue; } if (visitedFields.Contains(currentField)) { return(true); } visitedFields.Add(currentField); pendingValues.Push(currentField.Value); while (pendingValues.Count > 0) { JsonPlusValue currentValue = pendingValues.Pop(); foreach (IJsonPlusNode value in currentValue) { switch (value) { case JsonPlusLiteralValue _: break; case JsonPlusObject o: foreach (JsonPlusObjectMember f in o.Values) { if (visitedFields.Contains(f)) { return(true); } visitedFields.Add(f); pendingValues.Push(f.Value); } break; case JsonPlusArray a: foreach (JsonPlusValue item in a.GetArray()) { pendingValues.Push(item); } break; case JsonPlusSubstitution s: pendingSubs.Push(s); break; } } } } return(false); }