/// <summary> /// Ensure the following type can be serialized. If not, try to register appropriate serializer. /// This method can be recursive. /// </summary> /// <param name="type">The type.</param> public SerializableTypeInfo GenerateSerializer(TypeReference type, bool force = true, string profile = "Default", bool generic = false) { var serializableTypes = GetSerializableTypes(profile); // Already handled? SerializableTypeInfo serializableTypeInfo; if (serializableTypes.TryGetSerializableTypeInfo(type, generic, out serializableTypeInfo)) { return(serializableTypeInfo); } // Try to get one without generic if (generic && serializableTypes.TryGetSerializableTypeInfo(type, false, out serializableTypeInfo)) { return(serializableTypeInfo); } // TDOO: Array, List, Generic types, etc... (equivalent of previous serializer factories) var arrayType = type as ArrayType; if (arrayType != null) { // Only proceed if element type is serializable (and in Default profile, otherwise ElementType is enough) if (GenerateSerializer(arrayType.ElementType, force, profile) != null) { if (profile == "Default") { var arraySerializerType = StrideCoreModule.GetTypeResolved("Stride.Core.Serialization.Serializers.ArraySerializer`1"); var serializerType = new GenericInstanceType(arraySerializerType); serializerType.GenericArguments.Add(arrayType.ElementType); AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true), profile); return(serializableTypeInfo); } else { // Fallback to default return(GenerateSerializer(type, force, "Default")); } } return(null); } // Try to match with existing generic serializer (for List, Dictionary, etc...) var genericInstanceType = type as GenericInstanceType; if (genericInstanceType != null) { var elementType = genericInstanceType.ElementType; SerializableTypeInfo elementSerializableTypeInfo; if ((elementSerializableTypeInfo = GenerateSerializer(elementType, false, profile, true)) != null) { switch (elementSerializableTypeInfo.Mode) { case DataSerializerGenericMode.Type: { var serializerType = new GenericInstanceType(elementSerializableTypeInfo.SerializerType); serializerType.GenericArguments.Add(type); AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true) { ComplexSerializer = elementSerializableTypeInfo.ComplexSerializer }, profile); break; } case DataSerializerGenericMode.TypeAndGenericArguments: { var serializerType = new GenericInstanceType(elementSerializableTypeInfo.SerializerType); serializerType.GenericArguments.Add(type); foreach (var genericArgument in genericInstanceType.GenericArguments) { // Generate serializer for each generic argument //GenerateSerializer(genericArgument); serializerType.GenericArguments.Add(genericArgument); } AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true) { ComplexSerializer = elementSerializableTypeInfo.ComplexSerializer }, profile); break; } case DataSerializerGenericMode.GenericArguments: { var serializerType = new GenericInstanceType(elementSerializableTypeInfo.SerializerType); foreach (var genericArgument in genericInstanceType.GenericArguments) { // Generate serializer for each generic argument //GenerateSerializer(genericArgument); serializerType.GenericArguments.Add(genericArgument); } AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(serializerType, true) { ComplexSerializer = elementSerializableTypeInfo.ComplexSerializer }, profile); break; } default: throw new NotImplementedException(); } if (elementSerializableTypeInfo.ComplexSerializer) { ProcessComplexSerializerMembers(type, serializableTypeInfo); } return(serializableTypeInfo); } } // Check complex type definitions if (profile == "Default" && (serializableTypeInfo = FindSerializerInfo(type, generic)) != null) { return(serializableTypeInfo); } // Fallback to default if (profile != "Default") { return(GenerateSerializer(type, force, "Default", generic)); } // Part after that is only if a serializer is absolutely necessary. This is skipped when scanning normal assemblies type that might have nothing to do with serialization. if (!force) { return(null); } // Non instantiable type? (object, interfaces, abstract classes) // Serializer can be null since they will be inherited anyway (handled through MemberSerializer) var resolvedType = type.Resolve(); if (resolvedType.IsAbstract || resolvedType.IsInterface || resolvedType.FullName == typeof(object).FullName) { AddSerializableType(type, serializableTypeInfo = new SerializableTypeInfo(null, true), profile); return(serializableTypeInfo); } return(null); }
/// <summary> /// Finds the serializer information. /// </summary> /// <param name="type">The type.</param> /// <param name="generic">If set to true, when using <see cref="DataSerializerGenericMode.Type"/>, it will returns the generic version instead of actual type one.</param> /// <returns></returns> /// <exception cref="System.InvalidOperationException">Not sure how to process this inherited serializer</exception> internal SerializableTypeInfo FindSerializerInfo(TypeReference type, bool generic) { if (type == null || type.FullName == typeof(object).FullName || type.FullName == typeof(ValueType).FullName || type.IsGenericParameter) { return(null); } var resolvedType = type.Resolve(); // Nested type if (resolvedType.IsNested) { // Check public/private flags if (!resolvedType.IsNestedPublic && !resolvedType.IsNestedAssembly) { return(null); } } if (resolvedType.IsEnum) { // Enum // Let's generate a EnumSerializer var enumSerializerType = StrideCoreModule.GetTypeResolved("Stride.Core.Serialization.Serializers.EnumSerializer`1"); var serializerType = new GenericInstanceType(enumSerializerType); serializerType.GenericArguments.Add(type); var serializableTypeInfo = new SerializableTypeInfo(serializerType, true, DataSerializerGenericMode.None); AddSerializableType(type, serializableTypeInfo); return(serializableTypeInfo); } // 1. Check if there is a Serializable attribute // Note: Not anymore since we don't want all system types to have unknown complex serializers. //if (((resolvedType.Attributes & TypeAttributes.Serializable) == TypeAttributes.Serializable) || resolvedType.CustomAttributes.Any(x => x.AttributeType.FullName == typeof(SerializableAttribute).FullName)) //{ // serializerInfo.Serializable = true; // serializerInfo.ComplexSerializer = true; // serializerInfo.ComplexSerializerName = SerializerTypeName(resolvedType); // return serializerInfo; //} // 2.1. Check if there is DataSerializerAttribute on this type (if yes, it is serializable, but not a "complex type") var dataSerializerAttribute = resolvedType.CustomAttributes.FirstOrDefault( x => x.AttributeType.FullName == "Stride.Core.Serialization.DataSerializerAttribute"); if (dataSerializerAttribute != null) { var modeField = dataSerializerAttribute.Fields.FirstOrDefault(x => x.Name == "Mode"); var mode = (modeField.Name != null) ? (DataSerializerGenericMode)modeField.Argument.Value : DataSerializerGenericMode.None; var dataSerializerType = ((TypeReference)dataSerializerAttribute.ConstructorArguments[0].Value); // Reading from custom arguments doesn't have its ValueType properly set dataSerializerType = dataSerializerType.FixupValueType(); if (mode == DataSerializerGenericMode.Type || (mode == DataSerializerGenericMode.TypeAndGenericArguments && type is GenericInstanceType)) { var genericSerializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = true }; AddSerializableType(type, genericSerializableTypeInfo); var actualSerializerType = new GenericInstanceType(dataSerializerType); // Add Type as generic arguments actualSerializerType.GenericArguments.Add(type); // If necessary, add generic arguments too if (mode == DataSerializerGenericMode.TypeAndGenericArguments) { foreach (var genericArgument in ((GenericInstanceType)type).GenericArguments) { actualSerializerType.GenericArguments.Add(genericArgument); } } // Special case for GenericMode == DataSerializerGenericMode.Type: we store actual serializer instantiation in SerializerType (alongside the generic version in GenericSerializerType). var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, true); AddSerializableType(type, serializableTypeInfo); if (!generic) { return(serializableTypeInfo); } return(genericSerializableTypeInfo); } else { var serializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = false }; AddSerializableType(type, serializableTypeInfo); return(serializableTypeInfo); } } // 2.2. Check if SerializableExtendedAttribute is set on this class, or any of its base class with ApplyHierarchy var serializableExtendedAttribute = resolvedType.CustomAttributes.FirstOrDefault( x => x.AttributeType.FullName == "Stride.Core.DataContractAttribute"); if (dataSerializerAttribute == null && serializableExtendedAttribute != null) { // CHeck if ApplyHierarchy is active, otherwise it needs to be the exact type. var inherited = serializableExtendedAttribute.Properties.Where(x => x.Name == "Inherited") .Select(x => (bool)x.Argument.Value) .FirstOrDefault(); var serializableTypeInfo = CreateComplexSerializer(type); serializableTypeInfo.Inherited = inherited; // Process members ProcessComplexSerializerMembers(type, serializableTypeInfo); return(serializableTypeInfo); } // Check if parent type contains Inherited attribute var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType); if (parentType != null) { // Generate serializer for parent type var parentSerializableInfoType = GenerateSerializer(parentType.Resolve(), false, generic: true); // If Inherited flag is on, we also generate a serializer for this type if (parentSerializableInfoType != null && parentSerializableInfoType.Inherited) { if (parentSerializableInfoType.ComplexSerializer) { var serializableTypeInfo = CreateComplexSerializer(type); serializableTypeInfo.Inherited = true; // Process members ProcessComplexSerializerMembers(type, serializableTypeInfo); return(serializableTypeInfo); } else if (parentSerializableInfoType.Mode == DataSerializerGenericMode.Type || parentSerializableInfoType.Mode == DataSerializerGenericMode.TypeAndGenericArguments) { // Register generic version var genericSerializableTypeInfo = new SerializableTypeInfo(parentSerializableInfoType.SerializerType, true, parentSerializableInfoType.Mode); AddSerializableType(type, genericSerializableTypeInfo); if (!type.HasGenericParameters) { var actualSerializerType = new GenericInstanceType(parentSerializableInfoType.SerializerType); // Add Type as generic arguments actualSerializerType.GenericArguments.Add(type); // If necessary, add generic arguments too if (parentSerializableInfoType.Mode == DataSerializerGenericMode.TypeAndGenericArguments) { foreach (var genericArgument in ((GenericInstanceType)parentType).GenericArguments) { actualSerializerType.GenericArguments.Add(genericArgument); } } // Register actual type var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, true); AddSerializableType(type, serializableTypeInfo); if (!generic) { return(serializableTypeInfo); } } return(genericSerializableTypeInfo); } else { throw new InvalidOperationException("Not sure how to process this inherited serializer"); } } } return(null); }