/// <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); }
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; } }
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); } }