示例#1
0
        static void parseParameters(JProperty jpParameters, Method method, IDictionary<string, ParameterType> parameterTypes, Func<string, string> tokenLookup)
        {
            if (jpParameters.Value.Type != JTokenType.Object)
            {
                method.Errors.Add("The `parameters` property is expected to be of type object");
                return;
            }
            var joParameters = (JObject)jpParameters.Value;

            // Keep track of unique SQL parameter names:
            var sqlNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            // Parse parameter properties:
            method.Parameters = new Dictionary<string, Parameter>(joParameters.Count, StringComparer.OrdinalIgnoreCase);
            foreach (var jpParam in joParameters.Properties())
            {
                if (jpParam.Value.Type != JTokenType.Object)
                {
                    method.Errors.Add("Parameter property '{0}' is expected to be of type object".F(jpParam.Name));
                    continue;
                }

                var joParam = (JObject)jpParam.Value;
                var sqlName = getString(joParam.Property("sqlName")).Interpolate(tokenLookup);
                var sqlType = getString(joParam.Property("sqlType")).Interpolate(tokenLookup);
                var typeName = getString(joParam.Property("type")).Interpolate(tokenLookup);
                var isOptional = getBool(joParam.Property("optional")) ?? false;
                var desc = getString(joParam.Property("description"));
                object defaultValue = DBNull.Value;

                // Assign a default `sqlName` if null:
                if (sqlName == null) sqlName = "@" + jpParam.Name;
                // TODO: validate sqlName is valid SQL parameter identifier!

                if (sqlNames.Contains(sqlName))
                {
                    method.Errors.Add("Duplicate SQL parameter name (`sqlName`): '{0}'".F(sqlName));
                    continue;
                }

                var param = new Parameter()
                {
                    Name = jpParam.Name,
                    SqlName = sqlName,
                    Description = desc,
                    IsOptional = isOptional
                };

                if (sqlType != null)
                {
                    int? length;
                    int? scale;
                    var typeBase = parseSqlType(sqlType, out length, out scale);
                    var sqlDbType = getSqlType(typeBase);
                    if (!sqlDbType.HasValue)
                    {
                        method.Errors.Add("Unknown SQL type name '{0}' for parameter '{1}'".F(typeBase, jpParam.Name));
                        continue;
                    }
                    else
                    {
                        param.SqlType = new ParameterType()
                        {
                            TypeBase = typeBase,
                            SqlDbType = sqlDbType.Value,
                            Length = length,
                            Scale = scale
                        };
                    }
                }
                else
                {
                    ParameterType paramType;
                    if (!parameterTypes.TryGetValue(typeName, out paramType))
                    {
                        method.Errors.Add("Could not find parameter type '{0}' for parameter '{1}'".F(typeName, jpParam.Name));
                        continue;
                    }
                    param.Type = paramType;
                }

                if (isOptional)
                {
                    var jpDefault = joParam.Property("default");
                    if (jpDefault != null)
                    {
                        // Parse the default value into a SqlValue:
                        param.DefaultSQLValue = jsonToSqlValue(jpDefault.Value, param.SqlType ?? param.Type);
                        param.DefaultCLRValue = jsonToCLRValue(jpDefault.Value, param.SqlType ?? param.Type);
                    }
                    else
                    {
                        // Use null:
                        param.DefaultSQLValue = DBNull.Value;
                        param.DefaultCLRValue = null;
                    }
                }

                method.Parameters.Add(jpParam.Name, param);
            }
        }
示例#2
0
        static void parseQuery(JProperty jpQuery, Method method, Func<string, string> tokenLookup)
        {
            if (jpQuery.Value.Type != JTokenType.Object)
            {
                method.Errors.Add("The `query` property is expected to be an object");
                return;
            }

            var joQuery = (JObject)jpQuery.Value;
            method.Query = new Query();

            // Parse the separated form of a query; this ensures that a SELECT query form is constructed.

            // 'select' is required:
            method.Query.Select = getString(joQuery.Property("select")).Interpolate(tokenLookup);
            if (String.IsNullOrEmpty(method.Query.Select))
            {
                method.Errors.Add("A `select` clause is required");
            }

            // The rest are optional:
            var jpFrom = joQuery.Property("from");
            if (jpFrom != null && jpFrom.Value.Type == JTokenType.Array)
            {
                // If the "from" property is an array, treat it as a subquery with optional joins:
                // [
                //   { subquery } | "object1Name", "alias1Name"
                //  (optional):
                //   ,"join",        { subquery } | "object2Name", "alias2Name", "join condition"
                //   ,"left join",   { subquery } | "object3Name", "alias3Name", "join condition"
                //   ,"outer join",  { subquery } | "object4Name", "alias4Name", "join condition"
                //   ...
                // ]
                method.Query.From = parseQueryFrom((JArray)jpFrom.Value, method.Errors, String.Empty).Interpolate(tokenLookup);
            }
            else if (jpFrom != null && jpFrom.Value.Type == JTokenType.String)
            {
                // Otherwise, assume it's a string:
                method.Query.From = getString(jpFrom).Interpolate(tokenLookup);
            }
            method.Query.Where = getString(joQuery.Property("where")).Interpolate(tokenLookup);
            method.Query.GroupBy = getString(joQuery.Property("groupBy")).Interpolate(tokenLookup);
            method.Query.Having = getString(joQuery.Property("having")).Interpolate(tokenLookup);
            method.Query.OrderBy = getString(joQuery.Property("orderBy")).Interpolate(tokenLookup);
            method.Query.WithCTEidentifier = getString(joQuery.Property("withCTEidentifier")).Interpolate(tokenLookup);
            method.Query.WithCTEexpression = getString(joQuery.Property("withCTEexpression")).Interpolate(tokenLookup);

            // Parse "xmlns" dictionary of "prefix": "http://uri.example.org/namespace" properties for WITH XMLNAMESPACES:
            var xmlNamespaces = new Dictionary<string, string>(StringComparer.Ordinal);
            var jpXmlns = joQuery.Property("xmlns");
            if (jpXmlns != null)
            {
                var joXmlns = (JObject)jpXmlns.Value;
                foreach (var jpNs in joXmlns.Properties())
                {
                    var prefix = jpNs.Name;
                    var ns = getString(jpNs).Interpolate(tokenLookup);
                    xmlNamespaces.Add(prefix, ns);
                }
                if (xmlNamespaces.Count > 0) method.Query.XMLNamespaces = xmlNamespaces;
            }

            string withCTEidentifier, withCTEexpression, select, from;
            string where, groupBy, having, orderBy;

            try
            {
                // Strip out all SQL comments:
                withCTEidentifier = stripSQLComments(method.Query.WithCTEidentifier);
                withCTEexpression = stripSQLComments(method.Query.WithCTEexpression);
                select = stripSQLComments(method.Query.Select);
                from = stripSQLComments(method.Query.From);
                where = stripSQLComments(method.Query.Where);
                groupBy = stripSQLComments(method.Query.GroupBy);
                having = stripSQLComments(method.Query.Having);
                orderBy = stripSQLComments(method.Query.OrderBy);
            }
            catch (Exception ex)
            {
                method.Errors.Add(ex.Message);
                return;
            }

            // Allocate a StringBuilder with enough space to construct the query:
            StringBuilder qb = new StringBuilder(
                (withCTEidentifier ?? String.Empty).Length + (withCTEexpression ?? String.Empty).Length + ";WITH  AS ()\r\n".Length
              + (select ?? String.Empty).Length + "SELECT ".Length
              + (from ?? String.Empty).Length + "\r\nFROM ".Length
              + (where ?? String.Empty).Length + "\r\nWHERE ".Length
              + (groupBy ?? String.Empty).Length + "\r\nGROUP BY ".Length
              + (having ?? String.Empty).Length + "\r\nHAVING ".Length
              + (orderBy ?? String.Empty).Length + "\r\nORDER BY ".Length
            );

            try
            {
                // This is a very conservative approach and will lead to false-positives for things like EXISTS() and sub-queries:
                if (containsSQLkeywords(select, "from", "into", "where", "group", "having", "order", "for"))
                    method.Errors.Add("SELECT clause cannot contain FROM, INTO, WHERE, GROUP BY, HAVING, ORDER BY, or FOR");
                if (containsSQLkeywords(from, "where", "group", "having", "order", "for"))
                    method.Errors.Add("FROM clause cannot contain WHERE, GROUP BY, HAVING, ORDER BY, or FOR");
                if (containsSQLkeywords(where, "group", "having", "order", "for"))
                    method.Errors.Add("WHERE clause cannot contain GROUP BY, HAVING, ORDER BY, or FOR");
                if (containsSQLkeywords(groupBy, "having", "order", "for"))
                    method.Errors.Add("GROUP BY clause cannot contain HAVING, ORDER BY, or FOR");
                if (containsSQLkeywords(having, "order", "for"))
                    method.Errors.Add("HAVING clause cannot contain ORDER BY or FOR");
                if (containsSQLkeywords(orderBy, "for"))
                    method.Errors.Add("ORDER BY clause cannot contain FOR");
            }
            catch (Exception ex)
            {
                method.Errors.Add(ex.Message);
            }

            if (method.Errors.Count != 0)
                return;

            // Construct the query:
            bool didSemi = false;
            if (xmlNamespaces.Count > 0)
            {
                didSemi = true;
                qb.AppendLine(";WITH XMLNAMESPACES (");
                using (var en = xmlNamespaces.GetEnumerator())
                    for (int i = 0; en.MoveNext(); ++i)
                    {
                        var xmlns = en.Current;
                        qb.AppendFormat("  '{0}' AS {1}", xmlns.Value.Replace("\'", "\'\'"), xmlns.Key);
                        if (i < xmlNamespaces.Count - 1) qb.Append(",\r\n");
                        else qb.Append("\r\n");
                    }
                qb.Append(")\r\n");
            }
            if (!String.IsNullOrEmpty(withCTEidentifier) && !String.IsNullOrEmpty(withCTEexpression))
            {
                if (!didSemi) qb.Append(';');
                qb.AppendFormat("WITH {0} AS (\r\n{1}\r\n)\r\n", withCTEidentifier, withCTEexpression);
            }
            qb.AppendFormat("SELECT {0}", select);
            if (!String.IsNullOrEmpty(from)) qb.AppendFormat("\r\nFROM {0}", from);
            if (!String.IsNullOrEmpty(where)) qb.AppendFormat("\r\nWHERE {0}", where);
            if (!String.IsNullOrEmpty(groupBy)) qb.AppendFormat("\r\nGROUP BY {0}", groupBy);
            if (!String.IsNullOrEmpty(having)) qb.AppendFormat("\r\nHAVING {0}", having);
            if (!String.IsNullOrEmpty(orderBy)) qb.AppendFormat("\r\nORDER BY {0}", orderBy);

            // Assign the constructed query:
            method.Query.SQL = qb.ToString();
        }
示例#3
0
 IHttpResponseAction errorsMethod(SHA1Hashed<ServicesOffering> main, Service service, Method method)
 {
     return new JsonRootResponse(
         links: new RestfulLink[]
         {
         },
         meta: new
         {
             configHash = main.HashHexString,
             serviceName = service.Name,
             methodName = method.Name,
             methodErrors = method.Errors
         }
     );
 }
示例#4
0
        void parseMethods(JProperty jpMethods, Service svc, string connectionString, IDictionary<string, ParameterType> parameterTypes, IDictionary<string, Method> methods, Func<string, string> tokenLookup)
        {
            if (jpMethods.Value.Type != JTokenType.Object)
            {
                svc.Errors.Add("The `methods` property is expected to be of type object");
                return;
            }

            var joMethods = (JObject)jpMethods.Value;

            // Parse each method:
            foreach (var jpMethod in joMethods.Properties())
            {
                // Is the method set to null?
                if (jpMethod.Value.Type == JTokenType.Null)
                {
                    // Remove it:
                    methods.Remove(jpMethod.Name);
                    continue;
                }
                if (jpMethod.Value.Type != JTokenType.Object)
                {
                    svc.Errors.Add("The method property `{0}` is expected to be of type object".F(jpMethod.Name));
                    continue;
                }

                var joMethod = ((JObject)jpMethod.Value);

                // Create a clone of the inherited descriptor or a new descriptor:
                Method method;
                if (methods.TryGetValue(jpMethod.Name, out method))
                    method = method.Clone();
                else
                {
                    method = new Method()
                    {
                        Name = jpMethod.Name,
                        ConnectionString = connectionString,
                        Errors = new List<string>(5)
                    };
                }
                methods[jpMethod.Name] = method;
                method.Service = svc;

                Debug.Assert(method.Errors != null);

                // Parse the definition:

                method.Description = getString(joMethod.Property("description")).Interpolate(tokenLookup);
                method.DeprecatedMessage = getString(joMethod.Property("deprecated")).Interpolate(tokenLookup);

                // Parse connection:
                var jpConnection = joMethod.Property("connection");
                if (jpConnection != null)
                {
                    method.ConnectionString = parseConnection(jpConnection, method.Errors, (s) => s.Interpolate(tokenLookup));
                }

                // Parse the parameters:
                var jpParameters = joMethod.Property("parameters");
                if (jpParameters != null)
                {
                    parseParameters(jpParameters, method, parameterTypes, tokenLookup);
                }

                // Parse query:
                var jpQuery = joMethod.Property("query");
                if (jpQuery != null)
                {
                    parseQuery(jpQuery, method, tokenLookup);
                }

                if (method.Query == null)
                {
                    method.Errors.Add("No query specified");
                }

                // Parse result mapping:
                var jpMapping = joMethod.Property("result");
                if (jpMapping != null)
                {
                    var joMapping = (JObject)jpMapping.Value;
                    method.Mapping = parseMapping(joMapping, method.Errors);
                }
            } // foreach (var method)
        }
示例#5
0
        async Task<IHttpResponseAction> dataMethod(SHA1Hashed<ServicesOffering> main, Method method, System.Collections.Specialized.NameValueCollection queryString)
        {
            // Check for descriptor errors:
            if (method.Errors.Count > 0)
            {
                return new JsonRootResponse(
                    statusCode: 500,
                    statusDescription: "Bad method descriptor",
                    message: "Bad method descriptor",
                    meta: new
                    {
                        configHash = main.HashHexString,
                        serviceName = method.Service.Name,
                        methodName = method.Name,
                        errors = method.Errors.ToArray()
                    }
                );
            }

            // Check required parameters:
            if (method.Parameters != null)
            {
                // Create a hash set of the query-string parameter names:
                var q = new HashSet<string>(queryString.AllKeys, StringComparer.OrdinalIgnoreCase);

                // Create a list of missing required parameter names:
                var missingParams = new List<string>(method.Parameters.Count(p => !p.Value.IsOptional));
                missingParams.AddRange(
                    from p in method.Parameters
                    where !p.Value.IsOptional && !q.Contains(p.Key)
                    select p.Key
                );

                if (missingParams.Count > 0)
                    return new JsonRootResponse(
                        statusCode: 400,
                        statusDescription: "Missing required parameters",
                        message: "Missing required parameters",
                        meta: new
                        {
                            configHash = main.HashHexString,
                            serviceName = method.Service.Name,
                            methodName = method.Name
                        },
                        errors: new[]
                        {
                            new
                            {
                                missingParams = missingParams.ToDictionary(
                                    p => p,
                                    p => new ParameterSerialized(method.Parameters[p]),
                                    StringComparer.OrdinalIgnoreCase
                                )
                            }
                        }
                    );

                missingParams = null;
            }

            // Open a connection and execute the command:
            using (var conn = new SqlConnection(method.ConnectionString))
            using (var cmd = conn.CreateCommand())
            {
                var parameterValues = new Dictionary<string, ParameterValue>(method.Parameters == null ? 0 : method.Parameters.Count);

                // Add parameters:
                if (method.Parameters != null)
                {
                    foreach (var param in method.Parameters)
                    {
                        bool isValid = true;
                        string message = null;
                        object sqlValue, clrValue;
                        var paramType = (param.Value.SqlType ?? param.Value.Type);
                        string rawValue = queryString[param.Key];

                        if (param.Value.IsOptional & (rawValue == null))
                        {
                            // Use the default value if the parameter is optional and is not specified on the query-string:
                            sqlValue = param.Value.DefaultSQLValue;
                            clrValue = param.Value.DefaultCLRValue;
                        }
                        else
                        {
                            try
                            {
                                sqlValue = getSqlValue(paramType.SqlDbType, rawValue);
                                if (sqlValue == null)
                                {
                                    isValid = false;
                                    message = "Unsupported SQL type '{0}'".F(paramType.SqlDbType);
                                }
                            }
                            catch (Exception ex)
                            {
                                isValid = false;
                                sqlValue = DBNull.Value;
                                message = ex.Message;
                            }

                            try
                            {
                                clrValue = getCLRValue(paramType.SqlDbType, rawValue);
                            }
                            catch { clrValue = null; }
                        }

                        parameterValues.Add(param.Key, isValid ? new ParameterValue(clrValue) : new ParameterValue(message, rawValue));

                        // Add the SQL parameter:
                        var sqlprm = cmd.Parameters.Add(param.Value.Name, paramType.SqlDbType);
                        sqlprm.IsNullable = param.Value.IsOptional;
                        if (paramType.Length != null) sqlprm.Precision = (byte)paramType.Length.Value;
                        if (paramType.Scale != null) sqlprm.Scale = (byte)paramType.Scale.Value;
                        sqlprm.SqlValue = sqlValue;
                    }
                }

                // Abort if we have invalid parameters:
                var invalidParameters = parameterValues.Where(p => !p.Value.isValid);
                if (invalidParameters.Any())
                {
                    return new JsonRootResponse(
                        statusCode: 400,
                        statusDescription: "Invalid parameter value(s)",
                        message: "Invalid parameter value(s)",
                        meta: new
                        {
                            configHash = main.HashHexString,
                            serviceName = method.Service.Name,
                            methodName = method.Name
                        },
                        errors: invalidParameters.Select(p => (object)new { name = p.Key, attemptedValue = p.Value.attemptedValue, message = p.Value.message }).ToArray()
                    );
                }

                //cmd.CommandTimeout = 360;   // seconds
                cmd.CommandType = CommandType.Text;
                // Set TRANSACTION ISOLATION LEVEL and optionally ROWCOUNT before the query:
                const string setIsoLevel = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;\r\n";
                var sbCmd = new StringBuilder(setIsoLevel, setIsoLevel.Length + method.Query.SQL.Length);
                //if (rowLimit > 0)
                //    sbCmd.Append("SET ROWCOUNT {0};\r\n".F(rowLimit));
                sbCmd.Append(method.Query.SQL);
                cmd.CommandText = sbCmd.ToString();

                // Stopwatches used for precise timing:
                Stopwatch swOpenTime, swExecTime, swReadTime;

                swOpenTime = Stopwatch.StartNew();
                try
                {
                    // Open the connection asynchronously:
                    await conn.OpenAsync();
                    swOpenTime.Stop();
                }
                catch (Exception ex)
                {
                    swOpenTime.Stop();
                    return getErrorResponse(ex);
                }

                // Execute the query:
                SqlDataReader dr;
                swExecTime = Stopwatch.StartNew();
                try
                {
                    // Execute the query asynchronously:
                    dr = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection);
                    swExecTime.Stop();
                }
                catch (ArgumentException aex)
                {
                    swExecTime.Stop();
                    // SQL Parameter validation only gives `null` for `aex.ParamName`.
                    return new JsonRootResponse(400, aex.Message);
                }
                catch (Exception ex)
                {
                    swExecTime.Stop();
                    return getErrorResponse(ex);
                }

                swReadTime = Stopwatch.StartNew();
                try
                {
                    var results = await ReadResult(method, dr, RowMapperUseMapping);
                    swReadTime.Stop();

                    var meta = new MetadataSerialized
                    {
                        configHash = main.HashHexString,
                        serviceName = method.Service.Name,
                        methodName = method.Name,
                        deprecated = method.DeprecatedMessage,
                        parameters = parameterValues,
                        // Timings are in msec:
                        timings = new MetadataTimingsSerialized
                        {
                            open = Math.Round(swOpenTime.ElapsedTicks * 1000m / (decimal)Stopwatch.Frequency, 2),
                            exec = Math.Round(swExecTime.ElapsedTicks * 1000m / (decimal)Stopwatch.Frequency, 2),
                            read = Math.Round(swReadTime.ElapsedTicks * 1000m / (decimal)Stopwatch.Frequency, 2),
                            total = Math.Round((swOpenTime.ElapsedTicks + swExecTime.ElapsedTicks + swReadTime.ElapsedTicks) * 1000m / (decimal)Stopwatch.Frequency, 2),
                        }
                    };

                    return new JsonRootResponse(
                        links: new RestfulLink[]
                        {
                        },
                        meta: meta,
                        results: results
                    );
                }
                catch (JsonResultException jex)
                {
                    swReadTime.Stop();
                    return new JsonRootResponse(statusCode: jex.StatusCode, message: jex.Message);
                }
                catch (Exception ex)
                {
                    swReadTime.Stop();
                    return getErrorResponse(ex);
                }
            }
        }
示例#6
0
 IHttpResponseAction debugMethod(SHA1Hashed<ServicesOffering> main, Service service, Method method)
 {
     return new JsonRootResponse(
         links: new RestfulLink[]
         {
             RestfulLink.Create("self", "/debug/{0}/{1}".F(service.Name, method.Name), "self"),
             RestfulLink.Create("parent", "/debug/{0}".F(service.Name), "parent"),
             RestfulLink.Create("meta", "/meta/{0}/{1}".F(service.Name, method.Name)),
             RestfulLink.Create("errors", "/errors/{0}/{1}".F(service.Name, method.Name)),
             RestfulLink.Create("data", "/data/{0}/{1}".F(service.Name, method.Name))
         },
         meta: new
         {
             configHash = main.HashHexString,
             method = new MethodDebug(method)
         }
     );
 }
示例#7
0
        /// <summary>
        /// Reads the entire SqlDataReader asynchronously and returns the entire list of row objects when complete.
        /// </summary>
        /// <param name="method"></param>
        /// <param name="dr"></param>
        /// <param name="rowMapper"></param>
        /// <returns></returns>
        async Task<List<Dictionary<string, object>>> ReadResult(Method method, SqlDataReader dr, RowMapperDelegate rowMapper)
        {
            int fieldCount = dr.FieldCount;

            var names = new string[fieldCount];
            var columns = new object[fieldCount];
            for (int i = 0; i < fieldCount; ++i)
            {
                names[i] = dr.GetName(i);
            }

            var nameLookup = (
                from i in Enumerable.Range(0, fieldCount)
                select new { i, name = dr.GetName(i) }
            ).ToLookup(p => p.name, p => p.i);

            var list = new List<Dictionary<string, object>>();

            // Enumerate rows asynchronously:
            while (await dr.ReadAsync())
            {
                // Enumerate columns asynchronously:
                for (int i = 0; i < fieldCount; ++i)
                {
                    columns[i] = await dr.GetFieldValueAsync<object>(i);
                }

                // Map all the columns to a single object:
                var result = rowMapper(method, names, nameLookup, columns);
                list.Add(result);
            }
            return list;
        }
示例#8
0
        /// <summary>
        /// Maps columns from the result set using the mapping schema defined in the method descriptor.
        /// </summary>
        /// <param name="names"></param>
        /// <param name="ordinals"></param>
        /// <param name="method"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        Dictionary<string, object> RowMapperUseMapping(Method method, string[] names, ILookup<string, int> ordinals, object[] values)
        {
            // No custom mapping?
            if (method.Mapping == null)
            {
                var result = new Dictionary<string, object>();

                // Use a default mapping:
                for (int i = 0; i < names.Length; ++i)
                {
                    if (result.ContainsKey(names[i]))
                    {
                        // TODO: add a warning about duplicate column names.
                        continue;
                    }
                    result.Add(names[i], values[i]);
                }

                return result;
            }

            // We have a custom mapping:
            return mapColumns(method.Mapping, ordinals, values);
        }
示例#9
0
        /// <summary>
        /// Parses column names for '{' and '}' which are used to indicate nested object mapping.
        /// </summary>
        /// <param name="names"></param>
        /// <param name="ordinals"></param>
        /// <param name="method"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        Dictionary<string, object> RowMapperCurlyInflate(Method method, string[] names, ILookup<string, int> ordinals, object[] values)
        {
            var objStack = new Stack<Dictionary<string, object>>(3);
            var result = new Dictionary<string, object>();
            var addTo = result;

            // Enumerate columns asynchronously:
            for (int i = 0; i < values.Length; ++i)
            {
                object col = values[i];
                string name = names[i];

                // Opening or closing a sub-object?
                if (name.StartsWith("{") || name.StartsWith("}"))
                {
                    int n = 0;
                    while (n < name.Length)
                    {
                        // Allow any number of leading close-curlies:
                        if (name[n] == '}')
                        {
                            addTo = objStack.Pop();
                            ++n;
                            continue;
                        }

                        // Only one open-curly allowed at the end:
                        if (name[n] == '{')
                        {
                            var curr = addTo;
                            objStack.Push(addTo);
                            if (curr == null) break;

                            string objname = name.Substring(n + 1);

                            if (col == DBNull.Value)
                                addTo = null;
                            else
                                addTo = new Dictionary<string, object>();

                            if (curr.ContainsKey(objname))
                                throw new JsonResultException(500, "{0} key specified more than once".F(name));
                            curr.Add(objname, addTo);
                        }
                        break;
                    }

                    continue;
                }

                if (addTo == null) continue;
                addTo.Add(name, col);
            }

            if (objStack.Count != 0)
                throw new JsonResultException(500, "Too many open curlies in column list: {0}".F(objStack.Count));

            return result;
        }
示例#10
0
 internal MethodMetadata(Method desc)
 {
     this.desc = desc;
 }
示例#11
0
 internal MethodDebug(Method desc)
 {
     this.desc = desc;
 }