/// <summary>
        /// Build <see cref="JsonSerializerSettings" /> to use for serialization using Newtonsoft.
        /// </summary>
        /// <param name="serializationDirection">Direction of serialization.</param>
        /// <param name="formattingKind">Kind of formatting to use.</param>
        /// <returns>Prepared settings to use with Newtonsoft.</returns>
        public JsonSerializerSettings BuildJsonSerializerSettings(
            SerializationDirection serializationDirection,
            JsonFormattingKind formattingKind = JsonFormattingKind.Default)
        {
            (serializationDirection == SerializationDirection.Serialize || serializationDirection == SerializationDirection.Deserialize)
            .Named(Invariant($"{nameof(serializationDirection)}-must-be-{nameof(SerializationDirection.Serialize)}-or{nameof(SerializationDirection.Serialize)}"))
            .Must().BeTrue();

            var result = SerializationKindToSettingsSelectorByDirection[formattingKind](SerializationDirection.Serialize);

            var specifiedConverters = this.RegisteredConverters.Select(_ =>
                                                                       serializationDirection == SerializationDirection.Serialize
                    ? _.SerializingConverterBuilderFunction()
                    : _.DeserializingConverterBuilderFunction()).ToList();

            var defaultConverters = this.GetDefaultConverters(serializationDirection, formattingKind);

            var converters = new JsonConverter[0]
                             .Concat(specifiedConverters)
                             .Concat(defaultConverters)
                             .ToList();

            // TODO: We may need this sorted differently; as in does it need to reverse?
            result.Converters = converters;

            if (this.OverrideContractResolver != null && this.OverrideContractResolver.ContainsKey(serializationDirection))
            {
                var overrideResolver = this.OverrideContractResolver[serializationDirection];
                new { overrideResolver }.Must().NotBeNull();
                result.ContractResolver = overrideResolver.ContractResolverBuilderFunction();
            }

            return(result);
        }
예제 #2
0
        private void AddProperty(PropertyInfo propertyInfo, Type type, SerializationDirection direction = SerializationDirection.Both)
        {
            var name = PropertyScanningConvention.GetPropertyName(propertyInfo);

            if (ConstructedMetadata.PropertyGetters.ContainsKey(name) ||
                ConstructedMetadata.PropertySetters.ContainsKey(name))
            {
                throw new InvalidOperationException(string.Format("Property {0} is already registered on type {1}.", name, typeof(TResource)));
            }

            if (direction == SerializationDirection.Out || direction == SerializationDirection.Both)
            {
                ConstructedMetadata.PropertyGetters[name] = propertyInfo.GetValue;
            }

            if (direction == SerializationDirection.In || direction == SerializationDirection.Both)
            {
                ConstructedMetadata.PropertySetters[name] = propertyInfo.SetValue;
            }

            var instance   = Expression.Parameter(typeof(object), "i");
            var argument   = Expression.Parameter(typeof(object), "a");
            var setterCall = Expression.Call(
                Expression.Convert(instance, propertyInfo.DeclaringType),
                propertyInfo.GetSetMethod(),
                Expression.Convert(argument, propertyInfo.PropertyType));

            Expression <Action <object, object> > expression = Expression.Lambda <Action <object, object> >(setterCall, instance, argument);

            if (direction == SerializationDirection.In || direction == SerializationDirection.Both)
            {
                ConstructedMetadata.PropertySettersExpressions[name] = expression;
            }
        }
 private void InternalPropertyBagThrowOnUnregisteredTypeIfAppropriate(
     Type objectType,
     SerializationDirection serializationDirection,
     object objectToSerialize)
 {
     this.SerializationConfiguration.ThrowOnUnregisteredTypeIfAppropriate(objectType, serializationDirection, objectToSerialize);
 }
		public SerializationEventArgs(SerializationDirection direction, Stream serializationStream, object serializationObject)
		{
			if(serializationStream == null)
				throw new ArgumentNullException("serializationStream");

			_direction = direction;
			_serializationStream = serializationStream;
			_serializationObject = serializationObject;
		}
        public JsonSerializerSettings BuildAnonymousJsonSerializerSettings(
            SerializationDirection serializationDirection,
            JsonFormattingKind formattingKind = JsonFormattingKind.Default)
        {
            // this is a hack to not mess with casing since the case must match for dynamic deserialization...
            var result = SerializationKindToSettingsSelectorByDirection[formattingKind](serializationDirection);

            result.ContractResolver = new DefaultContractResolver();
            result.Converters       = this.GetDefaultConverters(serializationDirection, formattingKind);
            return(result);
        }
        public SerializationEventArgs(SerializationDirection direction, Stream serializationStream, object serializationObject)
        {
            if (serializationStream == null)
            {
                throw new ArgumentNullException("serializationStream");
            }

            _direction           = direction;
            _serializationStream = serializationStream;
            _serializationObject = serializationObject;
        }
 private void InternalJsonThrowOnUnregisteredTypeIfAppropriate(
     Type objectType,
     SerializationDirection serializationDirection,
     object objectToSerialize)
 {
     // this type needs no registration and has value in consistent escaping/encoding...
     if (objectType != typeof(string))
     {
         this.SerializationConfiguration.ThrowOnUnregisteredTypeIfAppropriate(objectType, serializationDirection, objectToSerialize);
     }
 }
        private IList <JsonConverter> GetDefaultConverters(SerializationDirection serializationDirection, JsonFormattingKind formattingKind)
        {
            switch (serializationDirection)
            {
            case SerializationDirection.Serialize:
                return(this.GetDefaultSerializingConverters(formattingKind));

            case SerializationDirection.Deserialize:
                return(this.GetDefaultDeserializingConverters());

            default:
                throw new NotSupportedException(Invariant($"{nameof(SerializationDirection)} value {serializationDirection} is not supported."));
            }
        }
        private void ValidateEnumerableElementsAreRegisteredIfApplicable(
            Type originalType,
            SerializationDirection serializationDirection,
            Type memberRuntimeType,
            object memberObject)
        {
            if (memberRuntimeType.IsArray || memberRuntimeType.IsClosedSystemCollectionType())
            {
                var enumerableObject = (IEnumerable)memberObject;

                foreach (var elementObject in enumerableObject)
                {
                    if (elementObject != null)
                    {
                        var elementObjectType = elementObject.GetType();

                        this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, elementObjectType, serializationDirection, elementObject);
                    }
                }
            }
            else if (memberRuntimeType.IsClosedSystemDictionaryType())
            {
                var dictionaryObject = (IDictionary)memberObject;

                foreach (var keyObject in dictionaryObject.Keys)
                {
                    if (keyObject != null)
                    {
                        var keyObjectType = keyObject.GetType();

                        this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, keyObjectType, serializationDirection, keyObject);
                    }
                }

                foreach (var valueObject in dictionaryObject.Values)
                {
                    if (valueObject != null)
                    {
                        var valueObjectType = valueObject.GetType();

                        this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, valueObjectType, serializationDirection, valueObject);
                    }
                }
            }
            else
            {
                // not applicable
            }
        }
        /// <summary>
        /// Gets the func that builds a <see cref="JsonConverter"/> for the specified <see cref="SerializationDirection"/>.
        /// </summary>
        /// <param name="serializationDirection">The serialization direction.</param>
        /// <returns>
        /// The func that builds a <see cref="JsonConvert"/> for the specified <see cref="SerializationDirection"/>.
        /// </returns>
        public Func <JsonConverter> GetJsonConverterBuilderFuncBySerializationDirection(
            SerializationDirection serializationDirection)
        {
            switch (serializationDirection)
            {
            case SerializationDirection.Serialize:
                return(this.SerializingConverterBuilderFunc);

            case SerializationDirection.Deserialize:
                return(this.DeserializingConverterBuilderFunc);

            default:
                throw new NotSupportedException(Invariant($"This {nameof(SerializationDirection)} is not supported: {serializationDirection}"));
            }
        }
        /// <summary>
        /// Build <see cref="JsonSerializerSettings" /> to use for serialization using Newtonsoft.
        /// </summary>
        /// <param name="serializationDirection">Direction of serialization.</param>
        /// <param name="jsonSerializationConfiguration">The serialization configuration in use.</param>
        /// <returns>
        /// Prepared settings to use with Newtonsoft.
        /// </returns>
        public JsonSerializerSettings BuildJsonSerializerSettings(
            SerializationDirection serializationDirection,
            JsonSerializationConfigurationBase jsonSerializationConfiguration)
        {
            if (serializationDirection == SerializationDirection.Unknown)
            {
                throw new ArgumentOutOfRangeException(Invariant($"'{nameof(serializationDirection)}' == '{SerializationDirection.Unknown}'"), (Exception)null);
            }

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

            var jsonFormattingKind = jsonSerializationConfiguration.JsonFormattingKind;

            var jsonSerializerSettingsBuilder = JsonFormattingKindToSettingsSelectorByDirection[jsonFormattingKind](SerializationDirection.Serialize);

            var result = jsonSerializerSettingsBuilder(() => this.RegisteredTypeToRegistrationDetailsMap);

            var specifiedConverters = this.jsonConverterBuilders.Select(_ => _.GetJsonConverterBuilderFuncBySerializationDirection(serializationDirection)()).ToList();

            var defaultConverters = this.GetDefaultConverters(serializationDirection, jsonFormattingKind);

            // Newtonsoft uses the converters in the order they are provided, so specifiedConverters will have priority because they come first
            var converters = new JsonConverter[0]
                             .Concat(specifiedConverters)
                             .Concat(defaultConverters)
                             .ToList();

            result.Converters = converters;

            if ((this.OverrideContractResolver != null) && this.OverrideContractResolver.ContainsKey(serializationDirection))
            {
                var overrideResolver = this.OverrideContractResolver[serializationDirection];

                if (overrideResolver == null)
                {
                    throw new ArgumentException(Invariant($"{nameof(overrideResolver)} is null"));
                }

                result.ContractResolver = overrideResolver.ContractResolverBuilder(() => this.RegisteredTypeToRegistrationDetailsMap);
            }

            return(result);
        }
        private void InternalThrowOnUnregisteredTypeIfAppropriate(
            Type originalType,
            Type typeToValidate,
            SerializationDirection serializationDirection,
            object objectToSerialize)
        {
            // For non-restricted types we need to validate the type itself as well as all ancestors.
            // This protects against the scenario where a derived type is registered, but it's
            // ancestor isn't.  In BSON, for example, the class map only covers the members
            // declared on the type itself.  So if BSON hasn't "seen" the ancestors, the right class
            // map will not be created/mapped to those ancestor types.
            // This approach is also particularly important for post-initialization registrations,
            // where serializes have no opportunity, during initialization, to "see" the closed generic
            // ancestors.
            // Note that ValidateMembersAreRegistered() will look for declared and non-declared members,
            // which is why we don't need to call ValidateTypeIsRegistered() on the ancestor types.
            var typeToValidateIncludingAncestors = IsRestrictedType(typeToValidate)
                ? new[] { typeToValidate }
                : new[] { typeToValidate }.Concat(typeToValidate.GetInheritancePath()).Except(new[] { typeof(object) }).ToArray();

            foreach (var localTypeToValidate in typeToValidateIncludingAncestors)
            {
                if (!this.validatedTypes.ContainsKey(localTypeToValidate))
                {
                    this.ValidateTypeIsRegistered(originalType, localTypeToValidate);

                    this.validatedTypes.TryAdd(localTypeToValidate, null);
                }
            }

            // We don't want to validate the members of restricted types like bool, List<SomeType>, etc.
            // ValidateTypeIsRegistered will pull out the type(s) of the array element, collection elements,
            // dictionary keys/values and validate them.  On serialization, objects having array/collection/dictionary
            // properties will have those enumerables iterated and their runtime types checked in ValidateMembersAreRegistered().
            // So if we get there with a restricted type, there's nothing to do.
            if (!IsRestrictedType(typeToValidate))
            {
                if (!this.TypesPermittedToHaveUnregisteredMembers.ContainsKey(typeToValidate))
                {
                    this.ValidateMembersAreRegistered(originalType, typeToValidate, serializationDirection, objectToSerialize);
                }
            }
        }
        private void AddProperty(PropertyInfo propertyInfo, SerializationDirection direction = SerializationDirection.Both)
        {
            var name = CamelCaseUtil.ToCamelCase(propertyInfo.Name);

            if (BuiltResourceMapping.PropertyGetters.ContainsKey(name) ||
                BuiltResourceMapping.PropertySetters.ContainsKey(name))
            {
                throw new InvalidOperationException(string.Format("Property {0} is already registered on type {1}.", name, typeof(TResource)));
            }

            if (direction == SerializationDirection.Out || direction == SerializationDirection.Both)
            {
                BuiltResourceMapping.PropertyGetters[name] = propertyInfo.GetValue;
            }

            if (direction == SerializationDirection.In || direction == SerializationDirection.Both)
            {
                BuiltResourceMapping.PropertySetters[name] =
                    !PropertyScanningConvention.IsLinkedResource(propertyInfo)
                    ? propertyInfo.SetValue
                    : (Action <object, object>)(Delegate)(propertyInfo.ToCompiledSetterAction <object, object>());
            }

            var instance   = Expression.Parameter(typeof(object), "i");
            var argument   = Expression.Parameter(typeof(object), "a");
            var setterCall = Expression.Call(
                Expression.Convert(instance, propertyInfo.DeclaringType),
                propertyInfo.GetSetMethod(),
                Expression.Convert(argument, propertyInfo.PropertyType));

            Expression <Action <object, object> > expression = Expression.Lambda <Action <object, object> >(setterCall, instance, argument);

            if (direction == SerializationDirection.In || direction == SerializationDirection.Both)
            {
                BuiltResourceMapping.PropertySettersExpressions[name] = expression;
            }
        }
예제 #14
0
        public ResourceConfigurationBuilder <TResource> WithSimpleProperty(Expression <Func <TResource, object> > propertyAccessor, SerializationDirection direction)
        {
            var propertyInfo = ExpressionUtils.GetPropertyInfoFromExpression(propertyAccessor);

            RemoveProperty(propertyInfo);
            AddProperty(propertyInfo, typeof(TResource), direction);
            return(this);
        }
        private void ValidateMembersAreRegistered(
            Type originalType,
            Type typeToValidate,
            SerializationDirection serializationDirection,
            object objectToSerialize)
        {
            // get or add members from cache
            if (!CachedTypeToAllFieldsAndPropertiesMemberInfoMap.ContainsKey(typeToValidate))
            {
                // note that there is some overlap between this and GetMemberTypesToInclude()
                // both are fetching property and field members of a type.  GetMemberTypesToInclude
                // uses the DeclaredOnly flag so there would be extra work to filter out the non-declared
                // members in GetMemberTypesToInclude().  Trying to harmonize this into a single cache
                // might introduce thread contention issues (potential a deadlock?) with syncConfigure.
                // Not worth the optimization.
                var memberInfosToAdd = typeToValidate
                                       .GetMembersFiltered(MemberRelationships.DeclaredInTypeOrAncestorTypes, MemberOwners.Instance, MemberAccessModifiers.All, MemberKinds.Field | MemberKinds.Property)
                                       .ToList();

                CachedTypeToAllFieldsAndPropertiesMemberInfoMap.TryAdd(typeToValidate, memberInfosToAdd);
            }

            var memberInfos = CachedTypeToAllFieldsAndPropertiesMemberInfoMap[typeToValidate];

            foreach (var memberInfo in memberInfos)
            {
                var memberDeclaredType = memberInfo.GetUnderlyingType();

                if ((serializationDirection == SerializationDirection.Deserialize) || (serializationDirection == SerializationDirection.Unknown))
                {
                    this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, memberDeclaredType, serializationDirection, null);
                }
                else if (serializationDirection == SerializationDirection.Serialize)
                {
                    object memberObject;

                    switch (memberInfo)
                    {
                    case PropertyInfo propertyInfo:
                        memberObject = propertyInfo.GetValue(objectToSerialize);
                        break;

                    case FieldInfo fieldInfo:
                        memberObject = fieldInfo.GetValue(objectToSerialize);
                        break;

                    default:
                        throw new NotSupportedException(Invariant($"This type of {nameof(MemberInfo)} is not supported: {memberInfo.GetType().ToStringReadable()}."));
                    }

                    if (memberObject == null)
                    {
                        // just recurse the declared types
                        this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, memberDeclaredType, SerializationDirection.Unknown, null);
                    }
                    else
                    {
                        var memberRuntimeType = memberObject.GetType();

                        this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, memberRuntimeType, serializationDirection, memberObject);

                        // in case they are equal, save a redundant call
                        if (memberDeclaredType != memberRuntimeType)
                        {
                            // recurse the declared type, which is not the runtime type
                            this.InternalThrowOnUnregisteredTypeIfAppropriate(originalType, memberDeclaredType, SerializationDirection.Unknown, null);
                        }

                        this.ValidateEnumerableElementsAreRegisteredIfApplicable(originalType, serializationDirection, memberRuntimeType, memberObject);
                    }
                }
                else
                {
                    throw new NotSupportedException("This serialization direction is not supported: " + serializationDirection);
                }
            }
        }
        public ResourceConfigurationBuilder <TResource, TController> WithSimpleProperty(Expression <Func <TResource, object> > propertyAccessor, SerializationDirection direction)
        {
            var propertyInfo = propertyAccessor.GetPropertyInfo();

            RemoveProperty(propertyInfo);
            AddProperty(propertyInfo, direction);
            return(this);
        }
        public void ThrowOnUnregisteredTypeIfAppropriate(
            Type type,
            SerializationDirection serializationDirection,
            object objectToSerialize)
        {
            // For serialization we are dealing with validating both declared and runtime types:
            //     We are holding an object and it's object.GetType(), we need to validate that type ahead of serialization.
            //     This means recursing thru the declared types AND runtime types of the original type's properties and fields.
            //     Further, for arrays, collections and dictionaries we need to recurse
            //     into the runtime types of the elements/keys/values and then consider the declared and runtime types
            //     of the element/keys/values fields and properties.  In that process there is certainly some redundant
            //     checks, but the heuristic would be harder to write in a correct way and anyways validatedTypes
            //     short-circuits a lot of validation.  After this process, we'll be 100% certain that all of the types involved
            //     with the object being serialized have been registered.  Because we are checking runtime types, we may encounter
            //     unregistered closed generic types whose generic type definitions ARE registered.  In that case, we call
            //     RegisterClosedGenericTypePostInitialization().  It's fine if, sometime later, we register the same closed generic
            //     type in a descendant config type, because the descendant will notify it's ancestors and the ancestors will only
            //     register the type if it is not yet registered.
            // For deserialization we are dealing with validating declared types:
            //     We validate that the declared type is registered and we recurse thru the declared types of the fields
            //     and properties and validate that they are registered as well.  Given this, upon deserialization
            //     the only unregistered types that we can encounter in the payload are concrete classes that are being
            //     assigned to an interface or base class type.  In this case, at serialization time, we would have written
            //     the assembly qualified name of the concrete type into the payload to facilitate de-serialization.
            //     So the type we resolve from the assembly qualified name is potentially unregistered, and thus we need to call
            //     THIS method on these types.  Assuming that the user is using the same config type for serialization and deserialization,
            //     serialization (per above) recurses thru all runtime types and would have thrown for unregistered, non-generic
            //     classes, and also throws if a generic class's generic type definition is not registered.
            //     As mentioned above closed generic classes will be registered if needed.  So its unlikely that calling this
            //     method to validate concrete classes being assigned to interface or base types will throw.
            //     What about generic classes that are NOT being deserialized into an interface or base class?
            //     If the top-level type, the type we are deserializing into, is a closed generic type then we'll register that type
            //     HERE in ValidateTypeIsRegistered() using RegisterClosedGenericTypePostInitialization(), if needed.
            //     Otherwise, if there is a field or property whose declared type is a closed generic, we will check that it's registered
            //     HERE in ValidateMembersAreRegistered() and if not call RegisterClosedGenericTypePostInitialization() if needed.
            if (type == null)
            {
                // this must be supported for serializing null
                // if type == null then objectToSerialize == null
                return;
            }

            if (type.ContainsGenericParameters)
            {
                throw new InvalidOperationException(Invariant($"Cannot serialize or deserialize an open type: {type.ToStringReadable()}."));
            }

            if (this.UnregisteredTypeEncounteredStrategy == UnregisteredTypeEncounteredStrategy.Attempt)
            {
                // Short-circuit attempt mode.  If not, then when we encounter a closed generic we will try to register
                // it, regardless of whether the generic type definition has been registered.  It's not clear whether attempt
                // should be registering any types at all.
                return;
            }

            this.InternalThrowOnUnregisteredTypeIfAppropriate(type, type, serializationDirection, objectToSerialize);

            // if serializing and the type is a System type (e.g. List<MyClass>)
            // then we need to iterate through the runtime types of the enumerable elements, if applicable
            if ((serializationDirection == SerializationDirection.Serialize) && type.IsSystemType())
            {
                this.ValidateEnumerableElementsAreRegisteredIfApplicable(type, serializationDirection, type, objectToSerialize);
            }
        }