Parses key value pair expressions from a string. - CSharp System

CSharp examples for System:String Parse

Description

Parses key value pair expressions from a string.

Demo Code

//  Copyright ? 2011, Grid Protection Alliance.  All Rights Reserved.
using System.Text.RegularExpressions;
using System.Text;
using System.IO;/*w  ww .  ja  va 2  s  .co m*/
using System.Globalization;
using System.ComponentModel;
using System.Collections.Generic;
using System;

public class Main{
        /// <summary>
        /// Parses key/value pair expressions from a string. Parameter pairs are delimited by <paramref name="keyValueDelimeter"/>
        /// and multiple pairs separated by <paramref name="parameterDelimeter"/>. Supports encapsulated nested expressions.
        /// </summary>
        /// <param name="value">String containing key/value pair expressions to parse.</param>
        /// <param name="parameterDelimeter">Character that delimits one key/value pair from another.</param>
        /// <param name="keyValueDelimeter">Character that delimits key from value.</param>
        /// <param name="startValueDelimeter">Optional character that marks the start of a value such that value could contain other
        /// <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> characters.</param>
        /// <param name="endValueDelimeter">Optional character that marks the end of a value such that value could contain other
        /// <paramref name="parameterDelimeter"/> or <paramref name="keyValueDelimeter"/> characters.</param>
        /// <param name="ignoreDuplicateKeys">Flag determines whether duplicates are ignored. If flag is set to <c>false</c> an
        /// <see cref="ArgumentException"/> will be thrown when all key parameters are not unique.</param>
        /// <returns>Dictionary of key/value pairs.</returns>
        /// <remarks>
        /// <para>
        /// Parses a string containing key/value pair expressions (e.g., "localPort=5001; transportProtocol=UDP; interface=0.0.0.0").
        /// This method treats all "keys" as case-insensitive. Nesting of key/value pair expressions is allowed by encapsulating the
        /// value using the <paramref name="startValueDelimeter"/> and <paramref name="endValueDelimeter"/> values (e.g., 
        /// "dataChannel={Port=-1;Clients=localhost:8800}; commandChannel={Port=8900}; dataFormat=FloatingPoint;"). There must be one
        /// <paramref name="endValueDelimeter"/> for each encountered <paramref name="startValueDelimeter"/> in the value or a
        /// <see cref="FormatException"/> will be thrown. Multiple levels of nesting is supported. If the <paramref name="ignoreDuplicateKeys"/>
        /// flag is set to <c>false</c> an <see cref="ArgumentException"/> will be thrown when all key parameters are not unique. Note
        /// that keys within nested expressions are considered separate key/value pair strings and are not considered when checking
        /// for duplicate keys.
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentNullException">value is null.</exception>
        /// <exception cref="ArgumentException">All delimiters must be unique -or- all keys must be unique when
        /// <paramref name="ignoreDuplicateKeys"/> is set to <c>false</c>.</exception>
        /// <exception cref="FormatException">Total nested key/value value pair expressions are mismatched -or- encountered
        /// <paramref name="endValueDelimeter"/> before <paramref name="startValueDelimeter"/>.</exception>
        public static Dictionary<string, string> ParseKeyValuePairs(this string value, char parameterDelimeter = ';', char keyValueDelimeter = '=', char startValueDelimeter = '{', char endValueDelimeter = '}', bool ignoreDuplicateKeys = true)
        {
            if (value == (string)null)
                throw new ArgumentNullException("value");

            if (parameterDelimeter == keyValueDelimeter ||
                parameterDelimeter == startValueDelimeter ||
                parameterDelimeter == endValueDelimeter ||
                keyValueDelimeter == startValueDelimeter ||
                keyValueDelimeter == endValueDelimeter ||
                startValueDelimeter == endValueDelimeter)
                throw new ArgumentException("All delimiters must be unique");

            Dictionary<string, string> keyValuePairs = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
            StringBuilder escapedValue = new StringBuilder();
            string escapedParameterDelimeter = parameterDelimeter.RegexEncode();
            string escapedKeyValueDelimeter = keyValueDelimeter.RegexEncode();
            string escapedStartValueDelimeter = startValueDelimeter.RegexEncode();
            string escapedEndValueDelimeter = endValueDelimeter.RegexEncode();
            string[] elements;
            string key, unescapedValue;
            bool valueEscaped = false;
            int delimiterDepth = 0;
            char character;

            // Escape any parameter or key/value delimiters within tagged value sequences
            //      For example, the following string:
            //          "normalKVP=-1; nestedKVP={p1=true; p2=false}")
            //      would be encoded as:
            //          "normalKVP=-1; nestedKVP=p1\\u003dtrue\\u003b p2\\u003dfalse")
            for (int x = 0; x < value.Length; x++)
            {
                character = value[x];

                if (character == startValueDelimeter)
                {
                    if (!valueEscaped)
                    {
                        valueEscaped = true;
                        continue;   // Don't add tag start delimiter to final value
                    }
                    else
                    {
                        // Handle nested delimiters
                        delimiterDepth++;
                    }
                }

                if (character == endValueDelimeter)
                {
                    if (valueEscaped)
                    {
                        if (delimiterDepth > 0)
                        {
                            // Handle nested delimiters
                            delimiterDepth--;
                        }
                        else
                        {
                            valueEscaped = false;
                            continue;   // Don't add tag stop delimiter to final value
                        }
                    }
                    else
                    {
                        throw new FormatException(string.Format("Failed to parse key/value pairs: invalid delimiter mismatch. Encountered end value delimiter \'{0}\' before start value delimiter \'{1}\'.", endValueDelimeter, startValueDelimeter));
                    }
                }

                if (valueEscaped)
                {
                    // Escape any delimiter characters inside nested key/value pair
                    if (character == parameterDelimeter)
                        escapedValue.Append(escapedParameterDelimeter);
                    else if (character == keyValueDelimeter)
                        escapedValue.Append(escapedKeyValueDelimeter);
                    else if (character == startValueDelimeter)
                        escapedValue.Append(escapedStartValueDelimeter);
                    else if (character == endValueDelimeter)
                        escapedValue.Append(escapedEndValueDelimeter);
                    else
                        escapedValue.Append(character);
                }
                else
                {
                    escapedValue.Append(character);
                }
            }

            if (delimiterDepth != 0 || valueEscaped)
            {
                // If value is still escaped, tagged expression was not terminated
                if (valueEscaped)
                    delimiterDepth = 1;

                throw new FormatException(string.Format("Failed to parse key/value pairs: invalid delimiter mismatch. Encountered more {0} than {1}.", delimiterDepth > 0 ? "start value delimiters \'" + startValueDelimeter + "\'" : "end value delimiters \'" + endValueDelimeter + "\'", delimiterDepth < 0 ? "start value delimiters \'" + startValueDelimeter + "\'" : "end value delimiters \'" + endValueDelimeter + "\'"));
            }

            // Parse key/value pairs from escaped value
            foreach (string parameter in escapedValue.ToString().Split(parameterDelimeter))
            {
                // Parse out parameter's key/value elements
                elements = parameter.Split(keyValueDelimeter);

                if (elements.Length == 2)
                {
                    // Get key expression
                    key = elements[0].ToString().Trim();

                    // Get unescaped value expression
                    unescapedValue = elements[1].ToString().Trim().
                        Replace(escapedParameterDelimeter, parameterDelimeter.ToString()).
                        Replace(escapedKeyValueDelimeter, keyValueDelimeter.ToString()).
                        Replace(escapedStartValueDelimeter, startValueDelimeter.ToString()).
                        Replace(escapedEndValueDelimeter, endValueDelimeter.ToString());

                    // Add key/value pair to dictionary
                    if (ignoreDuplicateKeys)
                    {
                        // Add or replace key elements with unescaped value
                        keyValuePairs[key] = unescapedValue;
                    }
                    else
                    {
                        // Add key elements with unescaped value throwing an exception for encountered duplicate keys
                        if (keyValuePairs.ContainsKey(key))
                            throw new ArgumentException(string.Format("Failed to parse key/value pairs: duplicate key encountered. Key \"{0}\" is not unique within the string: \"{1}\"", key, value));

                        keyValuePairs.Add(key, unescapedValue);
                    }
                }
            }

            return keyValuePairs;
        }
}

Related Tutorials