/// <summary>
        /// Queries the Cireson Portal for objects using specified criteria.
        /// </summary>
        /// <param name="authToken">AuthenticationToken to use</param>
        /// <param name="criteria">QueryCriteria rules</param>
        /// <returns>List of TypeProjections</returns>
        internal static async Task <List <T> > GetByCriteria <T>(AuthorizationToken authToken, QueryCriteria criteria) where T : TypeProjection
        {
            if (!authToken.IsValid)
            {
                throw new InvalidCredentialException("AuthorizationToken is not valid.");
            }

            try
            {
                // Initialize the HTTP helper and get going
                PortalHttpHelper helper = new PortalHttpHelper(authToken);
                string           result = await helper.PostAsync(GET_BY_CRITERIA_ENDPOINT, criteria.ToString());

                // TypeProjections have no set properties, so we deserialize to a list of ExpandoObjects
                ExpandoObjectConverter converter = new ExpandoObjectConverter();
                dynamic objectList = JsonConvert.DeserializeObject <List <ExpandoObject> >(result, converter);

                // Convert the ExpandoObjects into proper TypeProjection objects
                List <T> returnList = new List <T>();
                foreach (ExpandoObject obj in objectList)
                {
                    // Instantiate and add to the list
                    BindingFlags flags        = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
                    CultureInfo  culture      = null;
                    T            instanceType = (T)Activator.CreateInstance(typeof(T), flags, null, null, culture);

                    instanceType.CurrentObject  = obj.DeepCopy();
                    instanceType.OriginalObject = obj;
                    instanceType.ReadOnly       = false;

                    returnList.Add(instanceType);
                }

                return(returnList);
            }
            catch (Exception)
            {
                throw; // Rethrow exceptions
            }
        }
        /// <summary>
        /// Takes a QueryCriteria object and serializes it to JSON format
        /// </summary>
        /// <param name="writer">Writer in use</param>
        /// <param name="value">QueryCriteria object to serialize</param>
        /// <param name="serializer">Serializer in use</param>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            QueryCriteria criteria = (QueryCriteria)value;

            // Error checking
            if (criteria.Expressions.Count == 0)
            {
                throw new JsonSerializationException("Cannot serialize QueryCriteria with no expressions!");
            }
            else if (criteria.GroupingOperator == QueryCriteriaGroupingOperator.SimpleExpression && criteria.Expressions.Count > 1)
            {
                throw new JsonSerializationException("Cannot serialize QueryCriteria as SimpleExpression with more than one expression!");
            }
            else if ((criteria.GroupingOperator == QueryCriteriaGroupingOperator.And || criteria.GroupingOperator == QueryCriteriaGroupingOperator.Or) && criteria.Expressions.Count < 2)
            {
                throw new JsonSerializationException("Cannot serialize QueryCriteria as non-SimpleExpression with less than two expressions!");
            }
            else
            {
                // Check that all our expressions are valid
                foreach (QueryCriteriaExpression expression in criteria.Expressions)
                {
                    if (!expression.IsValid())
                    {
                        throw new JsonSerializationException("QueryCriteria contains an invalid expression!");
                    }
                }
            }

            // Let's go!
            writer.WriteStartObject();

            // Id Property
            writer.WritePropertyName("Id");
            writer.WriteValue(criteria.ProjectionID.ToString("D"));

            #region Criteria JSON object
            {
                writer.WritePropertyName("Criteria");
                writer.WriteStartObject();

                #region Base JSON object
                {
                    writer.WritePropertyName("Base");
                    writer.WriteStartObject();

                    #region Expression JSON object
                    {
                        writer.WritePropertyName("Expression");
                        writer.WriteStartObject();

                        // If we're not doing any kind of logical grouping, just dump out the SimpleExpression
                        if (criteria.GroupingOperator == QueryCriteriaGroupingOperator.SimpleExpression)
                        {
                            WriteSimpleExpression(writer, criteria.Expressions[0]);
                        }
                        else
                        {
                            // If we're doing logical grouping, we need to enumerate all the expressions and dump them out
                            #region And/Or object
                            {
                                writer.WritePropertyName(criteria.GroupingOperator.ToString());
                                writer.WriteStartObject();

                                #region Nested Expression object
                                {
                                    writer.WritePropertyName("Expression");
                                    writer.WriteStartArray();

                                    // Iterate over the expressions and write them
                                    foreach (QueryCriteriaExpression expression in criteria.Expressions)
                                    {
                                        writer.WriteStartObject();
                                        WriteSimpleExpression(writer, expression);
                                        writer.WriteEndObject();
                                    }

                                    writer.WriteEndArray();
                                }
                                #endregion Nested Expression object

                                writer.WriteEndObject();
                            }
                            #endregion
                        }

                        writer.WriteEndObject();
                    }
                    #endregion Expression JSON object

                    writer.WriteEndObject();
                }
                #endregion Base JSON object

                writer.WriteEndObject();
            }
            #endregion Criteria JSON object

            writer.WriteEndObject(); // JSON
        }