private OpenApiSchema CreateSchema(Type type, Queue <Type> referencedTypes)
        {
            // If Option<T> (F#), use the type argument
            if (type.IsFSharpOption())
            {
                type = type.GetGenericArguments()[0];
            }

            // Special handling for form binding types
            if (typeof(IFormFile).IsAssignableFrom(type))
            {
                return(new OpenApiSchema {
                    Type = "string", Format = "binary"
                });
            }

            var jsonContract = _jsonContractResolver.ResolveContract(type);

            var createReference = !_options.CustomTypeMappings.ContainsKey(type) &&
                                  type != typeof(object) &&
                                  (// Type describes an object
                jsonContract is JsonObjectContract ||
                // Type is self-referencing
                jsonContract.IsSelfReferencingArrayOrDictionary() ||
                // Type is enum and opt-in flag set
                (type.GetTypeInfo().IsEnum&& _options.UseReferencedDefinitionsForEnums));

            return(createReference
                ? CreateReferenceSchema(type, referencedTypes)
                : CreateInlineSchema(type, referencedTypes));
        }
Exemple #2
0
        public override bool CanCreateSchemaFor(Type type, out bool shouldBeReferenced)
        {
            if (_contractResolver.ResolveContract(type) is JsonObjectContract)
            {
                shouldBeReferenced = true;
                return(true);
            }

            shouldBeReferenced = false; return(false);
        }
Exemple #3
0
        public override bool CanCreateSchemaFor(Type type, out bool shouldBeReferenced)
        {
            if (_contractResolver.ResolveContract(type) is JsonDictionaryContract jsonDictionaryContract)
            {
                shouldBeReferenced = (jsonDictionaryContract.DictionaryValueType == type); // to avoid circular references
                return(true);
            }

            shouldBeReferenced = false; return(false);
        }
        private ApiType createType(Type type, Queue <Type> typeReferences)
        {
            var jsonContract = contractResolver.ResolveContract(type);

            var apiType = !primitiveTypeMappings.ContainsKey(type.Name) && type != typeof(object) && (jsonContract is JsonObjectContract || jsonContract.IsSelfReferencingArrayOrDictionary())
                ? createReferenceType(type, typeReferences)
                : createInlineType(type, typeReferences, jsonContract);

            return(apiType);
        }
Exemple #5
0
        private Schema CreateSchema(Type type)
        {
            var jsonContract = _jsonContractResolver.ResolveContract(type);

            var createReference = !_options.CustomTypeMappings.ContainsKey(type) &&
                                  type != typeof(object) &&
                                  (jsonContract is JsonObjectContract || jsonContract.IsSelfReferencingArrayOrDictionary());

            return(createReference ? CreateReferenceSchema(type) : CreateInlineSchema(type));
        }
Exemple #6
0
        private IEnumerable <JsonProperty> GetTypeProperties(Type type, IEnumerable <JsonProperty> excludedProperties)
        {
            if (_contractResolver.ResolveContract(type) is JsonObjectContract contract)
            {
                return(contract.Properties
                       .Where(p => excludedProperties.All(ep => ep.PropertyName != p.PropertyName))
                       .ToList());
            }

            return(new JsonProperty[] { });
        }
        public override bool CanCreateSchemaFor(Type type, out bool shouldBeReferenced)
        {
            if (_contractResolver.ResolveContract(type) is JsonObjectContract)
            {
                var shouldBePolymorphic = _generatorOptions.GeneratePolymorphicSchemas && _generatorOptions.SubTypesResolver(type).Any();
                shouldBeReferenced = !(type == typeof(object) || shouldBePolymorphic);
                return(true);
            }

            shouldBeReferenced = false; return(false);
        }
Exemple #8
0
        public override IAdapter Create(object target, IContractResolver contractResolver)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (contractResolver == null)
            {
                throw new ArgumentNullException(nameof(contractResolver));
            }

            var jsonContract = contractResolver.ResolveContract(target.GetType());

            if (target is IList)
            {
                return(new ListAdapter());
            }
            else if (jsonContract is JsonDictionaryContract jsonDictionaryContract)
            {
                var type = typeof(DictionaryAdapter <,>).MakeGenericType(jsonDictionaryContract.DictionaryKeyType, jsonDictionaryContract.DictionaryValueType);
                return((IAdapter)Activator.CreateInstance(type));
            }
            else if (jsonContract is JsonDynamicContract)
            {
                return(new DynamicObjectAdapter());
            }
            else
            {
                return(new AwarePocoAdapter());
            }
        }
Exemple #9
0
        protected override bool TryGetJsonProperty(object target, IContractResolver contractResolver, string segment,
                                                   out JsonProperty jsonProperty)
        {
            if (contractResolver.ResolveContract(target.GetType()) is JsonObjectContract jsonObjectContract)
            {
                var pocoProperty = jsonObjectContract
                                   .Properties
                                   .FirstOrDefault(p => string.Equals(p.PropertyName, segment, StringComparison.OrdinalIgnoreCase));

                if (pocoProperty != null)
                {
                    jsonProperty = pocoProperty;
                    if (jsonProperty.Writable && pocoProperty.AttributeProvider.GetAttributes(false)
                        .Any(a => a is ForbidPatchAttribute))
                    {
                        jsonProperty.Writable = false;
                    }

                    return(true);
                }
            }

            jsonProperty = null;
            return(false);
        }
        public OpenApiSchema GenerateSchemaFor(Type type, SchemaRepository schemaRepository)
        {
            if (_options.CustomTypeMappings.ContainsKey(type))
            {
                return(_options.CustomTypeMappings[type]());
            }

            if (KnownTypeMappings.ContainsKey(type))
            {
                return(KnownTypeMappings[type]());
            }

            if (type.IsNullable() || type.IsFSharpOption()) // unwrap nullables
            {
                type = type.GenericTypeArguments[0];
            }

            var jsonContract = _jsonContractResolver.ResolveContract(type);

            if (type.IsEnum || // enum
                (jsonContract is JsonObjectContract) || // regular object
                (jsonContract is JsonArrayContract && ((JsonArrayContract)jsonContract).CollectionItemType == jsonContract.UnderlyingType) || // self-referencing array
                (jsonContract is JsonDictionaryContract && ((JsonDictionaryContract)jsonContract).DictionaryValueType == jsonContract.UnderlyingType))    // self-referencing dictionary
            {
                return(GenerateReferenceSchemaFor(type, schemaRepository, jsonContract));
            }

            return(GenerateDefinitionSchemaFor(type, schemaRepository, jsonContract));
        }
Exemple #11
0
        /// <inheritdoc />
        public override JsonContract ResolveContract(Type type)
        {
            var contract = _existingContractResolver?.ResolveContract(type);

            if (contract?.Converter != null)
            {
                return(contract);
            }

            if (contract == null)
            {
                contract = base.ResolveContract(type);
            }

            if (type == typeof(DateTime) || type == typeof(DateTime?))
            {
                contract.Converter = _forceDateTimeKindOverride == DateTimeKind.Local ? JsonDateTimeTickConverter.LocalDateTimeKindDefault : JsonDateTimeTickConverter.Default;
            }
            else if (type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?))
            {
                contract.Converter = JsonDateTimeOffsetTickConverter.Default;
            }

            return(contract);
        }
Exemple #12
0
    protected static bool CanConvert(IContractResolver contractResolver, Type objectType, out Type elementType, out JsonProperty keyProperty)
    {
        if (objectType.IsArray)
        {
            // Not implemented for arrays, since they cannot be resized.
            elementType = null;
            keyProperty = null;
            return(false);
        }
        var elementTypes = objectType.GetIListItemTypes().ToList();

        if (elementTypes.Count != 1)
        {
            elementType = null;
            keyProperty = null;
            return(false);
        }
        elementType = elementTypes[0];
        var contract = contractResolver.ResolveContract(elementType) as JsonObjectContract;

        if (contract == null)
        {
            keyProperty = null;
            return(false);
        }
        keyProperty = contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonMergeKeyAttribute), true).Count > 0).SingleOrDefault();
        return(keyProperty != null);
    }
        public virtual bool TryTraverse(
            object target,
            string segment,
            IContractResolver contractResolver,
            out object nextTarget,
            out string errorMessage)
        {
            var contract   = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
            var key        = contract.DictionaryKeyResolver(segment);
            var dictionary = (IDictionary <TKey, TValue>)target;

            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
            {
                nextTarget = null;
                return(false);
            }

            if (dictionary.TryGetValue(convertedKey, out var valueAsT))
            {
                nextTarget   = valueAsT;
                errorMessage = null;
                return(true);
            }
            else
            {
                nextTarget   = null;
                errorMessage = null;
                return(false);
            }
        }
        private static IDataTypeInfo GetDataTypeInfo(Type propertyType, IContractResolver contractResolver)
        {
            bool IsNullableType(Type type) =>
            type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable <>);

            if (PrimitiveTypeMap.TryGetValue(propertyType, out DataType dataType))
            {
                return(DataTypeInfo.Simple(dataType));
            }
            else if (IsNullableType(propertyType))
            {
                return(GetDataTypeInfo(propertyType.GenericTypeArguments[0], contractResolver));
            }
            else if (TryGetEnumerableElementType(propertyType, out Type elementType))
            {
                IDataTypeInfo elementTypeInfo = GetDataTypeInfo(elementType, contractResolver);
                return(DataTypeInfo.AsCollection(elementTypeInfo));
            }
            else if (contractResolver.ResolveContract(propertyType) is JsonObjectContract jsonContract)
            {
                return(DataTypeInfo.Complex(DataType.Complex, propertyType, jsonContract));
            }
            else
            {
                return(DataTypeInfo.Unknown);
            }
        }
        public virtual bool TryGet(
            object target,
            string segment,
            IContractResolver contractResolver,
            out object value,
            out string errorMessage)
        {
            var contract   = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
            var key        = contract.DictionaryKeyResolver(segment);
            var dictionary = (IDictionary <TKey, TValue>)target;

            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
            {
                value = null;
                return(false);
            }

            if (!dictionary.TryGetValue(convertedKey, out var valueAsT))
            {
                value        = null;
                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
                return(false);
            }

            value        = valueAsT;
            errorMessage = null;
            return(true);
        }
        public virtual bool TryReplace(
            object target,
            string segment,
            IContractResolver contractResolver,
            object value,
            out string errorMessage)
        {
            var contract   = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
            var key        = contract.DictionaryKeyResolver(segment);
            var dictionary = (IDictionary <TKey, TValue>)target;

            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
            {
                return(false);
            }

            // As per JsonPatch spec, the target location must exist for remove to be successful
            if (!dictionary.ContainsKey(convertedKey))
            {
                errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment);
                return(false);
            }

            if (!TryConvertValue(value, out var convertedValue, out errorMessage))
            {
                return(false);
            }

            dictionary[convertedKey] = convertedValue;

            errorMessage = null;
            return(true);
        }
        public JsonContract ResolveContract(Type type)
        {
            if (type.GetTypeInfo().Assembly.Equals(_assembly))
                return _defaultContractSerializer.ResolveContract(type);

            return _camelCaseContractResolver.ResolveContract(type);
        }
 private async Task VisitPropertiesAsync(object obj, string path, ISet <object> checkedObjects)
 {
     if (_contractResolver.ResolveContract(obj.GetType()) is JsonObjectContract contract)
     {
         foreach (var property in contract.Properties.Where(p => !p.Ignored && p.ShouldSerialize?.Invoke(obj) != false))
         {
             var value = property.ValueProvider.GetValue(obj);
             if (value != null)
             {
                 await VisitAsync(value, path + "/" + property.PropertyName, property.PropertyName, checkedObjects, o => property.ValueProvider.SetValue(obj, o)).ConfigureAwait(false);
             }
         }
     }
     else
     {
         foreach (var member in ReflectionCache.GetPropertiesAndFields(obj.GetType()).Where(p =>
                                                                                            p.MemberInfo is PropertyInfo &&
                                                                                            (!(obj is JsonSchema4) || !_jsonSchemaProperties.Contains(p.MemberInfo.Name)) &&
                                                                                            (!(obj is IDictionary) || (p.MemberInfo.DeclaringType == obj.GetType())) && // only check additional properties of dictionary
                                                                                            p.CanRead &&
                                                                                            p.IsIndexer == false &&
                                                                                            p.CustomAttributes.JsonIgnoreAttribute == null))
         {
             var value = member.GetValue(obj);
             if (value != null)
             {
                 await VisitAsync(value, path + "/" + member.GetName(), member.GetName(), checkedObjects, o => member.SetValue(obj, o)).ConfigureAwait(false);
             }
         }
     }
 }
Exemple #19
0
        static bool CanConvert(IContractResolver contractResolver, Type objectType, out Type elementType, out JsonProperty keyProperty)
        {
            elementType = objectType.GetListType();
            if (elementType == null)
            {
                keyProperty = null;
                return(false);
            }

            var contract = contractResolver.ResolveContract(elementType) as JsonObjectContract;

            if (contract == null)
            {
                keyProperty = null;
                return(false);
            }
            //keyProperty = contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonMergeKeyAttribute), true).Count > 0).SingleOrDefault();
            keyProperty = null;
            foreach (var p in contract.Properties)
            {
                if (p.PropertyName == "Id")
                {
                    keyProperty = p;
                }
            }
            return(keyProperty != null);
        }
        protected override bool TryGetJsonProperty(object target, IContractResolver contractResolver, string segment, out JsonProperty jsonProperty)
        {
            if (contractResolver.ResolveContract(target.GetType()) is JsonObjectContract jsonObjectContract)
            {
                var pocoProperty = jsonObjectContract
                                   .Properties
                                   .FirstOrDefault(p => string.Equals(p.PropertyName, segment, StringComparison.OrdinalIgnoreCase));

                // Property must have PatchableAttribute or patch operation cannot be applied.
                if (pocoProperty != null)
                {
                    jsonProperty = pocoProperty;
                    // If the property is writable, but it doesn't have the PatchableAttribute, then make it not writable.
                    if (jsonProperty.Writable && !pocoProperty.AttributeProvider.GetAttributes(false).Any(a => a is PatchableAttribute))
                    {
                        jsonProperty.Writable = false;
                    }

                    return(true);
                }
            }

            jsonProperty = null;
            return(false);
        }
        public ApiModel ResolveApiModelFor(Type type)
        {
            // If it's nullable, use the inner value type
            var underlyingType = type.IsNullable(out Type valueType)
                ? valueType
                : type;

            var jsonContract = _jsonContractResolver.ResolveContract(underlyingType);

            if (jsonContract is JsonPrimitiveContract jsonPrimitiveContract)
            {
                return(ResolveApiPrimitive(jsonPrimitiveContract));
            }

            if (jsonContract is JsonDictionaryContract jsonDictionaryContract)
            {
                return(ResolveApiDictionary(jsonDictionaryContract));
            }

            if (jsonContract is JsonArrayContract jsonArrayContract)
            {
                return(ResolveApiArray(jsonArrayContract));
            }

            if (jsonContract is JsonObjectContract jsonObjectContract)
            {
                return(ResolveApiObject(jsonObjectContract));
            }

            return(new ApiModel(jsonContract.UnderlyingType));
        }
Exemple #22
0
        private static IEnumerable <Expression> CreateWriteValueExpressions(ParameterExpression writer, ParameterExpression value, ParameterExpression serializer, IContractResolver contractResolver)
        {
            var contract = contractResolver.ResolveContract(typeof(T)) as JsonObjectContract;

            if (contract != null)
            {
                foreach (var property in contract.Properties)
                {
                    var getValueMethod = typeof(IValueProvider).GetMethod("GetValue");
                    var valueProvider  = Expression.Constant(property.ValueProvider);
                    yield return
                        (Expression.Call(writer, LinkConverter.WritePropertyName,
                                         Expression.Constant(property.PropertyName)));

                    var getValue = Expression.Call(valueProvider, getValueMethod, value);
                    yield return(Expression.Call(serializer, LinkConverter.Serialize, writer, getValue));
                }
            }
            else
            {
                foreach (var info in typeof(T).GetProperties())
                {
                    yield return
                        (Expression.Call(writer, LinkConverter.WritePropertyName,
                                         Expression.Constant(ToCamelCase(info.Name))));

                    var getValue = Expression.Convert(Expression.Property(value, info), typeof(object));
                    yield return(Expression.Call(serializer, LinkConverter.Serialize, writer, getValue));
                }
            }
        }
        public virtual bool TryAdd(
            object target,
            string segment,
            IContractResolver contractResolver,
            object value,
            out string errorMessage)
        {
            var contract   = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType());
            var key        = contract.DictionaryKeyResolver(segment);
            var dictionary = (IDictionary <TKey, TValue>)target;

            // As per JsonPatch spec, if a key already exists, adding should replace the existing value
            if (!TryConvertKey(key, out var convertedKey, out errorMessage))
            {
                return(false);
            }

            if (!TryConvertValue(value, out var convertedValue, out errorMessage))
            {
                return(false);
            }

            dictionary[convertedKey] = convertedValue;
            errorMessage             = null;
            return(true);
        }
Exemple #24
0
        private static string GetJsonPropertyName(Type type, string propertyName)
        {
            var contract = DefaultResolver.ResolveContract(type) as JsonObjectContract;
            var property = contract.Properties.Single(p => p.UnderlyingName == propertyName);

            return(property.PropertyName);
        }
        private ICollection <FieldIndexingInfo> GenerateFieldIndexingInfos(Type type, int levels, Func <Type, JsonProperty, bool> propertySelector)
        {
            var fields = new Collection <FieldIndexingInfo>();

            if (_contractResolver.ResolveContract(type) is JsonObjectContract contract)
            {
                foreach (var property in contract.Properties)
                {
                    if (propertySelector?.Invoke(type, property) != false)
                    {
                        var searchAttribute = property.AttributeProvider
                                              .GetAttributes(true)
                                              .OfType <PictureparkSearchAttribute>()
                                              .SingleOrDefault();

                        var field = new FieldIndexingInfo
                        {
                            Id           = property.PropertyName,
                            SimpleSearch = searchAttribute?.SimpleSearch ?? false,
                            Index        = searchAttribute?.Index ?? false,
                            Boost        = searchAttribute?.Boost ?? 0.0,
                        };
                        fields.Add(field);

                        ApplyPictureparkSchemaIndexingAttribute(property, field);

                        if (levels > 0)
                        {
                            var propertyType = property.PropertyType.GenericTypeArguments.Any()
                                ? property.PropertyType.GenericTypeArguments.First()
                                : property.PropertyType;

                            var subFields = GenerateFieldIndexingInfos(propertyType, levels - 1, propertySelector);
                            if (subFields != null && subFields.Any())
                            {
                                field.RelatedSchemaIndexing = new SchemaIndexingInfo
                                {
                                    Fields = subFields
                                };
                            }
                        }
                    }
                }
            }

            return(fields);
        }
Exemple #26
0
        public void SerializeDynamicObject()
        {
            TestDynamicObject dynamicObject = new TestDynamicObject();

            dynamicObject.Explicit = true;

            dynamic d = dynamicObject;

            d.Int         = 1;
            d.Decimal     = 99.9d;
            d.ChildObject = new DynamicChildObject();

            Dictionary <string, object> values = new Dictionary <string, object>();

            IContractResolver   c = DefaultContractResolver.Instance;
            JsonDynamicContract dynamicContract = (JsonDynamicContract)c.ResolveContract(
                dynamicObject.GetType()
                );

            foreach (string memberName in dynamicObject.GetDynamicMemberNames())
            {
                object value;
                dynamicContract.TryGetMember(dynamicObject, memberName, out value);

                values.Add(memberName, value);
            }

            Assert.AreEqual(d.Int, values["Int"]);
            Assert.AreEqual(d.Decimal, values["Decimal"]);
            Assert.AreEqual(d.ChildObject, values["ChildObject"]);

            string json = JsonConvert.SerializeObject(dynamicObject, Formatting.Indented);

            StringAssert.AreEqual(
                @"{
  ""Explicit"": true,
  ""Decimal"": 99.9,
  ""Int"": 1,
  ""ChildObject"": {
    ""Text"": null,
    ""Integer"": 0
  }
}",
                json
                );

            TestDynamicObject newDynamicObject = JsonConvert.DeserializeObject <TestDynamicObject>(
                json
                );

            Assert.AreEqual(true, newDynamicObject.Explicit);

            d = newDynamicObject;

            Assert.AreEqual(99.9, d.Decimal);
            Assert.AreEqual(1, d.Int);
            Assert.AreEqual(dynamicObject.ChildObject.Integer, d.ChildObject.Integer);
            Assert.AreEqual(dynamicObject.ChildObject.Text, d.ChildObject.Text);
        }
 public JsonContract ResolveContract(Type type)
 {
     // We can't use camelcasing on any internal SignalR types
     // so these must be explicitly excluded
     return(type.Assembly.Equals(_assembly)
         ? _defaultContractSerializer.ResolveContract(type)
         : _camelCaseContractResolver.ResolveContract(type));
 }
Exemple #28
0
        private Schema CreateSchema(Type type, Queue <Type> referencedTypes)
        {
            var jsonContract = _jsonContractResolver.ResolveContract(type);

            var createReference = !_settings.CustomTypeMappings.ContainsKey(type) &&
                                  type != typeof(object) &&
                                  (// Type describes an object
                jsonContract is JsonObjectContract ||
                // Type is self-referencing
                jsonContract.IsSelfReferencingArrayOrDictionary() ||
                // Type is enum and opt-in flag set
                (type.IsEnumType() && _settings.UseReferencedDefinitionsForEnums));

            return(createReference
                ? CreateReferenceSchema(type, referencedTypes)
                : CreateInlineSchema(type, referencedTypes));
        }
Exemple #29
0
 public JsonContract ResolveContract(Type type)
 {
     if (type.Assembly.Equals(_signalRAssembly))
     {
         return(_defaultContractResolver.ResolveContract(type));
     }
     return(_camelCaseContractResolver.ResolveContract(type));
 }
 public JsonContract ResolveContract(Type type)
 {
     if (type.Assembly.Equals(assembly))
     {
         return(defaultContractSerializer.ResolveContract(type));
     }
     return(camelCaseContractResolver.ResolveContract(type));
 }
        public static JsonPatchProperty FindPropertyAndParent(
            object targetObject,
            string propertyPath,
            IContractResolver contractResolver)
        {
            try
            {
                var splitPath = propertyPath.Split('/');

                // skip the first one if it's empty
                var startIndex = (string.IsNullOrWhiteSpace(splitPath[0]) ? 1 : 0);

                for (int i = startIndex; i < splitPath.Length; i++)
                {
                    var jsonContract = (JsonObjectContract)contractResolver.ResolveContract(targetObject.GetType());

                    foreach (var property in jsonContract.Properties)
                    {
                        if (string.Equals(property.PropertyName, splitPath[i], StringComparison.OrdinalIgnoreCase))
                        {
                            if (i == (splitPath.Length - 1))
                            {
                                return new JsonPatchProperty(property, targetObject);
                            }
                            else
                            {
                                targetObject = property.ValueProvider.GetValue(targetObject);

                                // if property is of IList type then get the array index from splitPath and get the
                                // object at the indexed position from the list.
                                if (typeof(IList).GetTypeInfo().IsAssignableFrom(property.PropertyType.GetTypeInfo()))
                                {
                                    var index = int.Parse(splitPath[++i]);
                                    targetObject = ((IList)targetObject)[index];
                                }
                            }

                            break;
                        }
                    }
                }

                return null;
            }
            catch (Exception)
            {
                // will result in JsonPatchException in calling class, as expected
                return null;
            }
        }
        public ObjectTreeAnalysisResult(
            object objectToSearch,
            string propertyPath,
            IContractResolver contractResolver)
        {
            // construct the analysis result.

            // split the propertypath, and if necessary, remove the first
            // empty item (that's the case when it starts with a "/")
            var propertyPathTree = propertyPath.Split(
                new char[] { '/' },
                StringSplitOptions.RemoveEmptyEntries);

            // we've now got a split up property tree "base/property/otherproperty/..."
            int lastPosition = 0;
            object targetObject = objectToSearch;
            for (int i = 0; i < propertyPathTree.Length; i++)
            {
                lastPosition = i;

                // if the current target object is an ExpandoObject (IDictionary<string, object>),
                // we cannot use the ContractResolver.
                var dictionary = targetObject as IDictionary<string, object>;
                if (dictionary != null)
                {
                    // find the value in the dictionary
                    if (dictionary.ContainsCaseInsensitiveKey(propertyPathTree[i]))
                    {
                        var possibleNewTargetObject = dictionary.GetValueForCaseInsensitiveKey(propertyPathTree[i]);

                        // unless we're at the last item, we should set the targetobject
                        // to the new object.  If we're at the last item, we need to stop
                        if (i != propertyPathTree.Length - 1)
                        {
                            targetObject = possibleNewTargetObject;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    // if the current part of the path is numeric, this means we're trying
                    // to get the propertyInfo of a specific object in an array.  To allow
                    // for this, the previous value (targetObject) must be an IEnumerable, and
                    // the position must exist.

                    int numericValue = -1;
                    if (int.TryParse(propertyPathTree[i], out numericValue))
                    {
                        var element = GetElementAtFromObject(targetObject, numericValue);
                        if (element != null)
                        {
                            targetObject = element;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        var jsonContract = (JsonObjectContract)contractResolver.ResolveContract(targetObject.GetType());

                        // does the property exist?
                        var attemptedProperty = jsonContract
                            .Properties
                            .FirstOrDefault(p => string.Equals(p.PropertyName, propertyPathTree[i], StringComparison.OrdinalIgnoreCase));

                        if (attemptedProperty != null)
                        {
                            // unless we're at the last item, we should continue searching.
                            // If we're at the last item, we need to stop
                            if ((i != propertyPathTree.Length - 1))
                            {
                                targetObject = attemptedProperty.ValueProvider.GetValue(targetObject);
                            }
                        }
                        else
                        {
                            // property cannot be found, and we're not working with dynamics.
                            // Stop, and return invalid path.
                            break;
                        }
                    }
                }
            }

            if (propertyPathTree.Length - lastPosition != 1)
            {
                IsValidPathForAdd = false;
                IsValidPathForRemove = false;
                return;
            }

            // two things can happen now.  The targetproperty can be an IDictionary - in that
            // case, it's valid for add if there's 1 item left in the propertyPathTree.
            //
            // it can also be a property info.  In that case, if there's nothing left in the path
            // tree we're at the end, if there's one left we can try and set that.
            if (targetObject is IDictionary<string, object>)
            {
                UseDynamicLogic = true;

                Container = (IDictionary<string, object>)targetObject;
                IsValidPathForAdd = true;
                PropertyPathInParent = propertyPathTree[propertyPathTree.Length - 1];

                // to be able to remove this property, it must exist
                IsValidPathForRemove = Container.ContainsCaseInsensitiveKey(PropertyPathInParent);
            }
            else if (targetObject is IList)
            {
                UseDynamicLogic = false;

                int index;
                if (!Int32.TryParse(propertyPathTree[propertyPathTree.Length - 1], out index))
                {
                    // We only support indexing into a list
                    IsValidPathForAdd = false;
                    IsValidPathForRemove = false;
                    return;
                }

                IsValidPathForAdd = true;
                IsValidPathForRemove = ((IList)targetObject).Count > index;
                PropertyPathInParent = propertyPathTree[propertyPathTree.Length - 1];
            }
            else
            {
                UseDynamicLogic = false;

                var property = propertyPathTree[propertyPathTree.Length - 1];
                var jsonContract = (JsonObjectContract)contractResolver.ResolveContract(targetObject.GetType());
                var attemptedProperty = jsonContract
                    .Properties
                    .FirstOrDefault(p => string.Equals(p.PropertyName, property, StringComparison.OrdinalIgnoreCase));

                if (attemptedProperty == null)
                {
                    IsValidPathForAdd = false;
                    IsValidPathForRemove = false;
                }
                else
                {
                    IsValidPathForAdd = true;
                    IsValidPathForRemove = true;
                    JsonPatchProperty = new JsonPatchProperty(attemptedProperty, targetObject);
                    PropertyPathInParent = property;
                }
            }
        }
        public ScimPatchObjectAnalysis(
            ScimServerConfiguration serverConfiguration,
            object objectToSearch, 
            string filter, 
            IContractResolver contractResolver,
            Operation operation)
        {
            _ServerConfiguration = serverConfiguration;
            _ContractResolver = contractResolver;
            _Operation = operation;
            PatchMembers = new List<PatchMember>();

            /* 
                ScimFilter.cs will handle normalizing the actual path string. 
                
                Examples:
                "path":"members"
                "path":"name.familyName"
                "path":"addresses[type eq \"work\"]"
                "path":"members[value eq \"2819c223-7f76-453a-919d-413861904646\"]"
                "path":"members[value eq \"2819c223-7f76-453a-919d-413861904646\"].displayName"

                Once normalized, associate each resource member with its filter (if present).
                This is represented as a PathMember, which is essentially a tuple of <memberName, memberFilter?>
            */
            var pathTree = new ScimFilter(_ServerConfiguration.ResourceExtensionSchemas.Keys, filter).Paths.ToList();
            var lastPosition = 0;
            var nodes = GetAffectedMembers(pathTree, ref lastPosition, new Node(objectToSearch, null));
            
            if ((pathTree.Count - lastPosition) > 1)
            {
                IsValidPathForAdd = false;
                IsValidPathForRemove = false;
                return;
            }

            foreach (var node in nodes)
            {
                var attribute = node.Target as MultiValuedAttribute;
                JsonProperty attemptedProperty;
                if (attribute != null && PathIsMultiValuedEnumerable(pathTree[pathTree.Count - 1].Path, node, out attemptedProperty))
                {
                    /* Check if we're at a MultiValuedAttribute.
                       If so, then we'll return a special PatchMember.  This is because our actual target is 
                       an element within an enumerable. (e.g. User->Emails[element])
                       So a PatchMember must have three pieces of information: (following the example above)
                       > Parent (User)
                       > PropertyPath (emails)
                       > Actual Target (email instance/element)
                    */

                    UseDynamicLogic = false;
                    IsValidPathForAdd = true;
                    IsValidPathForRemove = true;
                    PatchMembers.Add(
                        new PatchMember(
                            pathTree[pathTree.Count - 1].Path,
                            new JsonPatchProperty(attemptedProperty, node.Parent),
                            node.Target));
                }
                else
                {
                    UseDynamicLogic = false;

                    var jsonContract = (JsonObjectContract)contractResolver.ResolveContract(node.Target.GetType());
                    attemptedProperty = jsonContract.Properties.GetClosestMatchProperty(pathTree[pathTree.Count - 1].Path);
                    if (attemptedProperty == null)
                    {
                        IsValidPathForAdd = false;
                        IsValidPathForRemove = false;
                    }
                    else
                    {
                        IsValidPathForAdd = true;
                        IsValidPathForRemove = true;
                        PatchMembers.Add(
                            new PatchMember(
                                pathTree[pathTree.Count - 1].Path,
                                new JsonPatchProperty(attemptedProperty, node.Target)));
                    }
                }
            }
        }