/// <summary>
        /// Loads the JSON data from a stream.
        /// </summary>
        /// <param name="stream">The JSON stream to read.</param>
        /// <param name="options">The parser options.</param>
        public static IDictionary <string, string> ParseStream(Stream stream, ParseToDictionaryOptions options = null)
        {
            var parserOptions = options ?? new ParseToDictionaryOptions();

            var data        = new SortedDictionary <string, string>(StringComparer.OrdinalIgnoreCase);
            var prefixStack = new Stack <string>();

            var jsonDocumentOptions = new JsonDocumentOptions
            {
                CommentHandling     = JsonCommentHandling.Skip,
                AllowTrailingCommas = true,
            };

            if (parserOptions.Parents != null)
            {
                foreach (var parent in parserOptions.Parents)
                {
                    prefixStack.Push(parent);
                }
            }

            using (var reader = new StreamReader(stream))
            {
                using (var doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions))
                {
                    if (doc.RootElement.ValueKind != JsonValueKind.Object)
                    {
                        throw new FormatException($"Top-level JSON element must be an object. Instead, '{doc.RootElement.ValueKind}' was found.");
                    }
                    VisitElement(doc.RootElement, prefixStack, data, parserOptions);
                }
            }

            return(data);
        }
        private static bool HasIdentifier(JsonElement element, Stack <string> prefixStack, ParseToDictionaryOptions parserOptions, out string identifier)
        {
            identifier = null;
            foreach (var property in element.EnumerateObject())
            {
                switch (property.Value.ValueKind)
                {
                case JsonValueKind.Number:
                case JsonValueKind.String:
                    identifier = property.Value.GetString();
                    if (!string.IsNullOrEmpty(identifier) && parserOptions.IsIdentifier(property.Name, prefixStack))
                    {
                        return(true);
                    }
                    break;

                default:
                    continue;
                }
            }
            return(false);
        }
        private static void VisitElement(JsonElement element, Stack <string> prefixStack, IDictionary <string, string> data, ParseToDictionaryOptions parserOptions)
        {
            var isEmpty = true;

            foreach (var property in element.EnumerateObject())
            {
                isEmpty = false;
                prefixStack.Push(property.Name.Capitalize());
                try
                {
                    VisitValue(property.Value, prefixStack, data, parserOptions);
                }
                finally
                {
                    prefixStack.Pop();
                }
            }

            if (isEmpty && prefixStack.Count > 0)
            {
                var key = string.Join(parserOptions.KeyDelimiter, prefixStack.Reverse());
                data[key] = null;
            }
        }
 /// <summary>
 /// Creates a new instance of <c>JsonStreamToDictionaryParser</c>.
 /// </summary>
 /// <param name="options">The parser options.</param>
 public JsonStreamToDictionaryParser(ParseToDictionaryOptions options = null)
 {
     _options = options;
 }
        private static void VisitValue(JsonElement value, Stack <string> prefixStack, IDictionary <string, string> data, ParseToDictionaryOptions parserOptions)
        {
            Debug.Assert(prefixStack.Count > 0);

            switch (value.ValueKind)
            {
            case JsonValueKind.Object:
                VisitElement(value, prefixStack, data, parserOptions);
                break;

            case JsonValueKind.Array:
                var index = 0;
                foreach (var arrayElement in value.EnumerateArray())
                {
                    if (HasIdentifier(arrayElement, prefixStack, parserOptions, out var identifier))
                    {
                        prefixStack.Push(identifier.Capitalize());
                    }
                    else
                    {
                        prefixStack.Push(index.ToString());
                        index++;
                    }
                    try
                    {
                        VisitValue(arrayElement, prefixStack, data, parserOptions);
                    }
                    finally
                    {
                        prefixStack.Pop();
                    }
                }
                break;

            case JsonValueKind.Number:
            case JsonValueKind.String:
            case JsonValueKind.True:
            case JsonValueKind.False:
            case JsonValueKind.Null:
                var key = string.Join(parserOptions.KeyDelimiter, prefixStack.Reverse());
                if (data.ContainsKey(key))
                {
                    throw new FormatException($"A duplicate key '{key}' was found.");
                }
                data[key] = value.ToString();
                break;

            default:
                throw new FormatException($"Unsupported JSON token '{value.ValueKind}' was found.");
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Loads the XML data from a stream.
        /// </summary>
        /// <param name="stream">The xml stream to read.</param>
        /// <param name="options">The parser options.</param>
        public static IDictionary <string, string> ParseStream(Stream stream, ParseToDictionaryOptions options = null)
        {
            var parserOptions = options ?? new ParseToDictionaryOptions();

            var data = new SortedDictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            var readerSettings = new XmlReaderSettings()
            {
                CloseInput       = false, // caller will close the stream
                DtdProcessing    = DtdProcessing.Prohibit,
                IgnoreComments   = true,
                IgnoreWhitespace = true
            };

            stream.Position = 0;

            // ReSharper disable once ConvertToUsingDeclaration
            using (var reader = XmlReader.Create(stream, readerSettings))
            {
                var prefixStack = new Stack <string>();

                if (parserOptions.Parents != null)
                {
                    foreach (var parent in parserOptions.Parents)
                    {
                        prefixStack.Push(parent);
                    }
                }

                SkipUntilRootElement(reader);

                // We process the root element individually since it doesn't contribute to prefix
                ProcessAttributes(reader, prefixStack, data, AddNamePrefix, parserOptions);
                ProcessAttributes(reader, prefixStack, data, AddAttributePair, parserOptions);

                var preNodeType = reader.NodeType;
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                    case XmlNodeType.Element:
                        prefixStack.Push(reader.LocalName.Capitalize());
                        ProcessAttributes(reader, prefixStack, data, AddNamePrefix, parserOptions);
                        ProcessAttributes(reader, prefixStack, data, AddAttributePair, parserOptions);

                        // If current element is self-closing
                        if (reader.IsEmptyElement)
                        {
                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (prefixStack.Any())
                        {
                            // If this EndElement node comes right after an Element node,
                            // it means there is no text/CDATA node in current element
                            if (preNodeType == XmlNodeType.Element)
                            {
                                var key = string.Join(parserOptions.KeyDelimiter, prefixStack.Reverse());
                                data[key] = string.Empty;
                            }

                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.CDATA:
                    case XmlNodeType.Text:
                    {
                        var key = string.Join(parserOptions.KeyDelimiter, prefixStack.Reverse());
                        if (data.ContainsKey(key))
                        {
                            throw new FormatException($"A duplicate key '{key}' was found. {GetLineInfo(reader)}");
                        }

                        data[key] = reader.Value;
                        break;
                    }

                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.ProcessingInstruction:
                    case XmlNodeType.Comment:
                    case XmlNodeType.Whitespace:
                        // Ignore certain types of nodes
                        break;

                    default:
                        throw new FormatException($"Unsupported node type '{reader.NodeType}' was found. {GetLineInfo(reader)}");
                    }
                    preNodeType = reader.NodeType;
                    // If this element is a self-closing element,
                    // we pretend that we just processed an EndElement node
                    // because a self-closing element contains an end within itself
                    if (preNodeType == XmlNodeType.Element &&
                        reader.IsEmptyElement)
                    {
                        preNodeType = XmlNodeType.EndElement;
                    }
                }
            }

            return(data);
        }
Esempio n. 7
0
        // Common attributes contribute to key-value pairs
        // This method adds a key-value pair if current node in reader represents a common attribute
        private static void AddAttributePair(XmlReader reader, Stack <string> prefixStack,
                                             IDictionary <string, string> data, XmlWriter writer, ParseToDictionaryOptions options)
        {
            //if (options.IsIndexAttribute(reader.LocalName, prefixStack))
            //{
            //    return;
            //}

            prefixStack.Push(reader.LocalName.Capitalize());
            var key = string.Join(options.KeyDelimiter, prefixStack.Reverse());

            if (data.ContainsKey(key))
            {
                throw new FormatException($"A duplicate key '{key}' was found. {GetLineInfo(reader)}");
            }

            data[key] = reader.Value;

            prefixStack.Pop();
        }
Esempio n. 8
0
        // The special attribute "Name" only contributes to prefix
        // This method adds a prefix if current node in reader represents a "Name" attribute
        private static void AddNamePrefix(XmlReader reader, Stack <string> prefixStack,
                                          IDictionary <string, string> data, XmlWriter writer, ParseToDictionaryOptions options)
        {
            string prefix = null;

            if (prefixStack.Any())
            {
                prefix = prefixStack.Pop();
            }

            if (!options.IsIdentifier(reader.LocalName, prefixStack))
            {
                if (prefix != null)
                {
                    prefixStack.Push(prefix);
                }
                return;
            }

            prefixStack.Push(reader.Value);
        }
Esempio n. 9
0
        private static void ProcessAttributes(XmlReader reader, Stack <string> prefixStack, IDictionary <string, string> data,
                                              Action <XmlReader, Stack <string>, IDictionary <string, string>, XmlWriter, ParseToDictionaryOptions> act, ParseToDictionaryOptions options, XmlWriter writer = null)
        {
            for (int i = 0; i < reader.AttributeCount; i++)
            {
                reader.MoveToAttribute(i);

                // If there is a namespace attached to current attribute
                if (!string.IsNullOrEmpty(reader.NamespaceURI))
                {
                    throw new FormatException($"XML namespaces are not supported. {GetLineInfo(reader)}");
                }

                act(reader, prefixStack, data, writer, options);
            }

            // Go back to the element containing the attributes we just processed
            reader.MoveToElement();
        }