示例#1
0
        /// <summary>
        /// Returns the <see cref="JsonPlusObjectMember"/> associated with the <see cref="JsonPlusPath"/> specified.
        /// </summary>
        /// <param name="path">The path to the member to return.</param>
        /// <param name="result">If the member is returned successfully, this parameter will contain the result <see cref="JsonPlusObjectMember"/>. This parameter should be passed uninitialized.</param>
        /// <returns>`true` if the <see cref="JsonPlusObject"/> contains the <see cref="JsonPlusObjectMember"/> associated with <paramref name="path"/>. Otherwise, `false`.</returns>
        public bool TryGetMember(JsonPlusPath path, out JsonPlusObjectMember result)
        {
            result = null;
            if (path == null || path.Count == 0)
            {
                return(false);
            }

            int            pathIndex     = 0;
            JsonPlusObject currentObject = this;

            while (true)
            {
                string key = path[pathIndex];

                if (!currentObject.TryGetValue(key, out var field))
                {
                    return(false);
                }

                if (pathIndex >= path.Count - 1)
                {
                    result = field;
                    return(true);
                }

                if (field.Type != JsonPlusType.Object)
                {
                    return(false);
                }

                currentObject = field.GetObject();
                pathIndex     = pathIndex + 1;
            }
        }
示例#2
0
        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);
        }
示例#3
0
        private static void Flatten(IJsonPlusNode node)
        {
            if (!(node is JsonPlusValue v))
            {
                return;
            }

            switch (v.Type)
            {
            case JsonPlusType.Object:
                JsonPlusObject o = v.GetObject();
                v.Clear();
                v.Add(o);
                foreach (JsonPlusObjectMember item in o.Values)
                {
                    Flatten(item);
                }
                break;

            case JsonPlusType.Array:
                List <IJsonPlusNode> a = v.GetArray();
                v.Clear();
                JsonPlusArray newArray = new JsonPlusArray(v);
                foreach (IJsonPlusNode item in a)
                {
                    Flatten(item);
                    newArray.Add(item);
                }
                v.Add(newArray);
                break;

            case JsonPlusType.Literal:
                if (v.Count == 1)
                {
                    return;
                }

                string value = v.GetString();
                v.Clear();
                if (value == null)
                {
                    v.Add(new NullValue(v));
                }
                else if (value.NeedTripleQuotes())
                {
                    v.Add(new TripleQuotedStringValue(v, value));
                }
                else if (value.NeedQuotes())
                {
                    v.Add(new QuotedStringValue(v, value));
                }
                else
                {
                    v.Add(new UnquotedStringValue(v, value));
                }
                break;
            }
        }
示例#4
0
        /// <see cref="IJsonPlusNode.Clone(IJsonPlusNode)"/>
        public IJsonPlusNode Clone(IJsonPlusNode newParent)
        {
            JsonPlusObject clone = new JsonPlusObject(newParent);

            foreach (KeyValuePair <string, JsonPlusObjectMember> kvp in this)
            {
                clone[kvp.Key] = (JsonPlusObjectMember)kvp.Value.Clone(clone);
            }
            return(clone);
        }
示例#5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JsonPlusObjectMember"/> class.
        /// </summary>
        /// <param name="path">The path to this member in the data tree.</param>
        /// <param name="parent">The parent container of this object instance.</param>
        public JsonPlusObjectMember(JsonPlusPath path, JsonPlusObject parent)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            Path            = new JsonPlusPath(path);
            Parent          = parent;
            _internalValues = new List <JsonPlusValue>();
        }
示例#6
0
        /// <summary>
        /// Returns the merged <see cref="JsonPlusObject"/> that backs the <see cref="JsonPlusObjectMember"/> associated with the key specified.
        /// </summary>
        /// <param name="key">The key associated with the member to return.</param>
        /// <param name="result">If the member is returned successfully, this parameter will contain the result <see cref="JsonPlusObjectMember"/>. This parameter should be passed uninitialized.</param>
        /// <returns>`true` if the <see cref="JsonPlusObject"/> contains the <see cref="JsonPlusObjectMember"/> associated with <paramref name="key"/>, and the <see cref="JsonPlusObjectMember.Type"/> property
        /// is <see cref="JsonPlusType.Object"/>. Otherwise, `false`.</returns>
        public bool TryGetObject(string key, out JsonPlusObject result)
        {
            result = null;
            if (!TryGetMember(key, out JsonPlusObjectMember field))
            {
                return(false);
            }

            result = field.GetObject();
            return(true);
        }
示例#7
0
        internal void EnsureMemberIsObject()
        {
            if (Type == JsonPlusType.Object)
            {
                return;
            }

            JsonPlusValue  v = new JsonPlusValue(this);
            JsonPlusObject o = new JsonPlusObject(v);

            v.Add(o);
            _internalValues.Add(v);
        }
示例#8
0
        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);
        }
示例#9
0
        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]);
        }
示例#10
0
 /// <summary>
 /// Merge the members of a <see cref="JsonPlusObject"/> into this instance.
 /// </summary>
 /// <param name="other">The <see cref="JsonPlusObject"/> to merge with this instance.</param>
 public virtual void Merge(JsonPlusObject other)
 {
     string[] keys = other.Keys.ToArray();
     foreach (string key in keys)
     {
         if (ContainsKey(key))
         {
             JsonPlusObjectMember thisItem  = this[key];
             JsonPlusObjectMember otherItem = other[key];
             if (thisItem.Type == JsonPlusType.Object &&
                 otherItem.Type == JsonPlusType.Object)
             {
                 thisItem.GetObject().Merge(otherItem.GetObject());
                 continue;
             }
         }
         this[key] = other[key];
     }
 }
示例#11
0
        /// <summary>
        /// Returns the <see cref="JsonPlusObjectMember"/> associated with the <see cref="JsonPlusPath"/> specified.
        /// </summary>
        /// <param name="path">The path to the field to return.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="path"/> specified is `null`.</exception>
        /// <exception cref="ArgumentException">The <paramref name="path"/> specified is empty.</exception>
        /// <exception cref="KeyNotFoundException">The key does not exist in the <see cref="JsonPlusObject"/> instance.</exception>
        /// <exception cref="JsonPlusException">The <paramref name="path"/> specified is invalid.</exception>
        /// <returns>The <see cref="JsonPlusObjectMember"/> associated with <paramref name="path"/>.</returns>
        public JsonPlusObjectMember GetMember(JsonPlusPath path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (path.Count == 0)
            {
                throw new ArgumentException(RS.PathIsEmpty, nameof(path));
            }

            int            pathIndex     = 0;
            JsonPlusObject currentObject = this;

            while (true)
            {
                string key = path[pathIndex];

                if (!currentObject.TryGetValue(key, out JsonPlusObjectMember field))
                {
                    throw new KeyNotFoundException(string.Format(RS.ObjectMemberNotFoundByPath,
                                                                 key, new JsonPlusPath(path.GetRange(0, pathIndex + 1)).Value));
                }

                if (pathIndex >= path.Count - 1)
                {
                    return(field);
                }

                if (field.Type != JsonPlusType.Object)
                {
                    throw new JsonPlusException(string.Format(RS.ObjectMemberInPathNotObject,
                                                              new JsonPlusPath(path.GetRange(0, pathIndex + 1)).Value));
                }

                currentObject = field.GetObject();
                pathIndex     = pathIndex + 1;
            }
        }
示例#12
0
        internal List <JsonPlusObjectMember> TraversePath(JsonPlusPath path)
        {
            List <JsonPlusObjectMember> result = new List <JsonPlusObjectMember>();
            int            pathLength          = 1;
            JsonPlusObject currentObject       = this;

            while (true)
            {
                JsonPlusObjectMember child = currentObject.GetOrCreateKey(new JsonPlusPath(path.GetRange(0, pathLength)));
                result.Add(child);

                pathLength++;
                if (pathLength > path.Count)
                {
                    return(result);
                }

                child.EnsureMemberIsObject();

                // cannot use child.GetObject() because it would return a merged object, which
                // breaks autoref with the parent object
                currentObject = child.Value.GetObject();
            }
        }
示例#13
0
 /// <see cref="JsonPlusObject.Merge(JsonPlusObject)"/>
 public override void Merge(JsonPlusObject other)
 {
     ((JsonPlusObjectMember)Parent).Value.Add(other.Clone(((JsonPlusObjectMember)Parent).Value));
     base.Merge(other);
 }
示例#14
0
        // The owner in this context can be either an object or an array.
        private JsonPlusObject ParseObject(IJsonPlusNode owner)
        {
            JsonPlusObject jpObject = new JsonPlusObject(owner);

            if (_tokens.Current.Type != TokenType.StartOfObject &&
                _tokens.Current.Type != TokenType.LiteralValue &&
                _tokens.Current.Type != TokenType.Include)
            {
                throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                          TokenType.StartOfObject, TokenType.LiteralValue,
                                                                                          _tokens.Current.Type));
            }

            bool headless = true;

            if (_tokens.Current.Type == TokenType.StartOfObject)
            {
                headless = false;
                ConsumeWhitelines();
            }

            IJsonPlusNode lastValue = null;
            bool          parsing   = true;

            while (parsing)
            {
                switch (_tokens.Current.Type)
                {
                case TokenType.Include:
                case TokenType.OptionalInclude:
                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                                  TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                                                  _tokens.Current.Type));
                    }

                    lastValue = ParseInclude(jpObject);
                    break;

                case TokenType.LiteralValue:
                    if (_tokens.Current.IsNonSignificant())
                    {
                        ConsumeWhitespace();
                    }
                    if (_tokens.Current.Type != TokenType.LiteralValue)
                    {
                        break;
                    }

                    if (lastValue != null)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                                  TokenType.ArraySeparator, TokenType.EndOfLine,
                                                                                                  _tokens.Current.Type));
                    }

                    lastValue = ParseObjectMember(jpObject);
                    break;

                // TODO: can an object be declared floating without being assigned to a field?
                //case TokenType.StartOfObject:
                case TokenType.Comment:
                case TokenType.EndOfLine:
                    switch (lastValue)
                    {
                    case null:
                        ConsumeWhitelines();
                        break;

                    case JsonPlusObjectMember _:
                        break;

                    default:
                        ((JsonPlusValue)jpObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.ArraySeparator:
                    switch (lastValue)
                    {
                    case null:
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenWith2AltInObject,
                                                                                                  TokenType.Assignment, TokenType.StartOfObject,
                                                                                                  _tokens.Current.Type));

                    case JsonPlusObjectMember _:
                        break;

                    default:
                        ((JsonPlusValue)jpObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    ConsumeWhitelines();
                    break;

                case TokenType.EndOfObject:
                case TokenType.EndOfFile:
                    if (headless && _tokens.Current.Type != TokenType.EndOfFile)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInObject, TokenType.EndOfFile, _tokens.Current.Type));
                    }

                    if (!headless && _tokens.Current.Type != TokenType.EndOfObject)
                    {
                        throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.UnexpectedTokenInObject, TokenType.EndOfFile, _tokens.Current.Type));
                    }

                    switch (lastValue)
                    {
                    case null:
                        break;

                    case JsonPlusObjectMember _:
                        break;

                    default:
                        ((JsonPlusValue)jpObject.Parent).Add(lastValue.GetObject());
                        break;
                    }
                    lastValue = null;
                    parsing   = false;
                    break;

                default:
                    throw JsonPlusParserException.Create(_tokens.Current, Path, string.Format(RS.ErrAtUnexpectedTokenInObject, _tokens.Current.Type));
                }
            }

            if (_tokens.Current.Type == TokenType.EndOfObject)
            {
                _tokens.Next();
            }

            return(jpObject);
        }