internal void SerializeContents(WorkflowMarkupSerializationManager serializationManager, object obj, XmlWriter writer, bool dictionaryKey) { if (serializationManager == null) throw new ArgumentNullException("serializationManager"); if (obj == null) throw new ArgumentNullException("obj"); if (writer == null) throw new ArgumentNullException("writer"); WorkflowMarkupSerializer serializer = null; try { //Now get the serializer to persist the properties, if the serializer is not found then we dont serialize the properties serializer = serializationManager.GetSerializer(obj.GetType(), typeof(WorkflowMarkupSerializer)) as WorkflowMarkupSerializer; } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); return; } if (serializer == null) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailableForSerialize, obj.GetType().FullName))); return; } try { serializer.OnBeforeSerialize(serializationManager, obj); } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); return; } Hashtable allProperties = new Hashtable(); ArrayList complexProperties = new ArrayList(); IDictionary<DependencyProperty, object> dependencyProperties = null; List<PropertyInfo> properties = new List<PropertyInfo>(); List<EventInfo> events = new List<EventInfo>(); Hashtable designTimeTypeNames = null; // Serialize the extended properties for primitive types also if (obj.GetType().IsPrimitive || obj.GetType() == typeof(string) || obj.GetType() == typeof(decimal) || obj.GetType() == typeof(DateTime) || obj.GetType() == typeof(TimeSpan) || obj.GetType().IsEnum || obj.GetType() == typeof(Guid)) { if (obj.GetType() == typeof(char) || obj.GetType() == typeof(byte) || obj.GetType() == typeof(System.Int16) || obj.GetType() == typeof(decimal) || obj.GetType() == typeof(DateTime) || obj.GetType() == typeof(TimeSpan) || obj.GetType().IsEnum || obj.GetType() == typeof(Guid)) { //These non CLS-compliant are not supported in the XmlWriter if ((obj.GetType() != typeof(char)) || (char)obj != '\0') { //These non CLS-compliant are not supported in the XmlReader string stringValue = String.Empty; if (obj.GetType() == typeof(DateTime)) { stringValue = ((DateTime)obj).ToString("o", CultureInfo.InvariantCulture); } else { TypeConverter typeConverter = TypeDescriptor.GetConverter(obj.GetType()); if (typeConverter != null && typeConverter.CanConvertTo(typeof(string))) stringValue = typeConverter.ConvertTo(null, CultureInfo.InvariantCulture, obj, typeof(string)) as string; else stringValue = Convert.ToString(obj, CultureInfo.InvariantCulture); } writer.WriteValue(stringValue); } } else if (obj.GetType() == typeof(string)) { string attribValue = obj as string; attribValue = attribValue.Replace('\0', ' '); if (!(attribValue.StartsWith("{", StringComparison.Ordinal) && attribValue.EndsWith("}", StringComparison.Ordinal))) writer.WriteValue(attribValue); else writer.WriteValue("{}" + attribValue); } else { writer.WriteValue(obj); } // For Key properties, we don;t want to get the extended properties if (!dictionaryKey) properties.AddRange(serializationManager.GetExtendedProperties(obj)); } else { // Serialize properties //We first get all the properties, once we have them all, we start distinguishing between //simple and complex properties, the reason for that is XmlWriter needs to write attributes //first and elements later // Dependency events are treated as the same as dependency properties. try { if (obj is DependencyObject && ((DependencyObject)obj).UserData.Contains(UserDataKeys.DesignTimeTypeNames)) designTimeTypeNames = ((DependencyObject)obj).UserData[UserDataKeys.DesignTimeTypeNames] as Hashtable; dependencyProperties = serializer.GetDependencyProperties(serializationManager, obj); properties.AddRange(serializer.GetProperties(serializationManager, obj)); // For Key properties, we don;t want to get the extended properties if (!dictionaryKey) properties.AddRange(serializationManager.GetExtendedProperties(obj)); events.AddRange(serializer.GetEvents(serializationManager, obj)); } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); return; } } if (dependencyProperties != null) { // For attached properties that does not have a corresponding real property on the object, if the value is a design time // type, it may not be set through dependency property SetValue, therefore will not be present in the dependencyProperties // collection, we'll have to get the dependency property object ourselves. if (designTimeTypeNames != null) { foreach (object key in designTimeTypeNames.Keys) { DependencyProperty dependencyProperty = key as DependencyProperty; if (dependencyProperty != null && !dependencyProperties.ContainsKey(dependencyProperty)) dependencyProperties.Add(dependencyProperty, designTimeTypeNames[dependencyProperty]); } } // Add all dependency properties to the master collection. foreach (DependencyProperty dependencyProperty in dependencyProperties.Keys) { string propertyName = String.Empty; if (dependencyProperty.IsAttached) { string prefix = String.Empty; XmlQualifiedName qualifiedName = serializationManager.GetXmlQualifiedName(dependencyProperty.OwnerType, out prefix); propertyName = qualifiedName.Name + "." + dependencyProperty.Name; } else { propertyName = dependencyProperty.Name; } if (dependencyProperty.IsAttached || !dependencyProperty.DefaultMetadata.IsMetaProperty) allProperties.Add(propertyName, dependencyProperty); } } if (properties != null) { foreach (PropertyInfo propInfo in properties) { // Do not serialize properties that have corresponding dynamic properties. if (propInfo != null && !allProperties.ContainsKey(propInfo.Name)) allProperties.Add(propInfo.Name, propInfo); } } if (events != null) { foreach (EventInfo eventInfo in events) { // Do not serialize events that have corresponding dynamic properties. if (eventInfo != null && !allProperties.ContainsKey(eventInfo.Name)) allProperties.Add(eventInfo.Name, eventInfo); } } using (ContentProperty contentProperty = new ContentProperty(serializationManager, serializer, obj)) { foreach (object propertyObj in allProperties.Values) { string propertyName = String.Empty; object propertyValue = null; Type propertyInfoType = null; try { if (propertyObj is PropertyInfo) { PropertyInfo property = propertyObj as PropertyInfo; // If the property has parameters we can not serialize it , we just move on. ParameterInfo[] indexParameters = property.GetIndexParameters(); if (indexParameters != null && indexParameters.Length > 0) continue; propertyName = property.Name; propertyValue = null; if (property.CanRead) { propertyValue = property.GetValue(obj, null); if (propertyValue == null && TypeProvider.IsAssignable(typeof(Type), property.PropertyType)) { // See if we have a design time value for the property DependencyProperty dependencyProperty = DependencyProperty.FromName(property.Name, property.ReflectedType); if (dependencyProperty != null) propertyValue = Helpers.GetDesignTimeTypeName(obj, dependencyProperty); if (propertyValue == null) { string key = property.ReflectedType.FullName + "." + property.Name; propertyValue = Helpers.GetDesignTimeTypeName(obj, key); } if (propertyValue != null) propertyValue = new TypeExtension((string)propertyValue); } } propertyInfoType = property.PropertyType; } else if (propertyObj is EventInfo) { EventInfo evt = propertyObj as EventInfo; propertyName = evt.Name; propertyValue = WorkflowMarkupSerializationHelpers.GetEventHandlerName(obj, evt.Name); if ((propertyValue == null || (propertyValue is string && string.IsNullOrEmpty((string)propertyValue))) && obj is DependencyObject) { // The object is not created through deserialization, we should check to see if the delegate is // created and added to list. We can only serialize the handler if its target type is the same // as the activity type that's be designed. DependencyProperty dependencyProperty = DependencyProperty.FromName(propertyName, obj.GetType()); if (dependencyProperty != null) { Activity activity = serializationManager.Context[typeof(Activity)] as Activity; Delegate handler = ((DependencyObject)obj).GetHandler(dependencyProperty) as Delegate; if (handler != null && activity != null && TypeProvider.Equals(handler.Target.GetType(), Helpers.GetRootActivity(activity).GetType())) propertyValue = handler; } } propertyInfoType = evt.EventHandlerType; } else if (propertyObj is DependencyProperty) { DependencyProperty dependencyProperty = propertyObj as DependencyProperty; propertyName = dependencyProperty.Name; propertyValue = dependencyProperties[dependencyProperty]; propertyInfoType = dependencyProperty.PropertyType; } } catch (Exception e) { while (e is TargetInvocationException && e.InnerException != null) e = e.InnerException; serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerPropertyGetFailed, new object[] { propertyName, obj.GetType().FullName, e.Message }))); continue; } if (propertyObj is PropertyInfo && contentProperty.Property == (PropertyInfo)propertyObj) continue; Type propertyValueType = null; if (propertyValue != null) { propertyValue = GetMarkupExtensionFromValue(propertyValue); propertyValueType = propertyValue.GetType(); } else if (propertyObj is PropertyInfo) { propertyValue = new NullExtension(); propertyValueType = propertyValue.GetType(); Attribute[] attributes = Attribute.GetCustomAttributes(propertyObj as PropertyInfo, typeof(DefaultValueAttribute), true); if (attributes.Length > 0) { DefaultValueAttribute defaultValueAttr = attributes[0] as DefaultValueAttribute; if (defaultValueAttr.Value == null) propertyValue = null; } } if (propertyValue != null) propertyValueType = propertyValue.GetType(); //Now get the serializer to persist the properties, if the serializer is not found then we dont serialize the properties serializationManager.Context.Push(propertyObj); WorkflowMarkupSerializer propValueSerializer = null; try { propValueSerializer = serializationManager.GetSerializer(propertyValueType, typeof(WorkflowMarkupSerializer)) as WorkflowMarkupSerializer; } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); serializationManager.Context.Pop(); continue; } if (propValueSerializer == null) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailableForSerialize, propertyValueType.FullName))); serializationManager.Context.Pop(); continue; } // ask serializer if we can serialize try { if (propValueSerializer.ShouldSerializeValue(serializationManager, propertyValue)) { //NOTE: THE FOLLOWING CONDITION ABOUT propertyInfoType != typeof(object) is VALID AS WE SHOULD NOT SERIALIZE A PROPERTY OF TYPE OBJECT TO STRING //IF WE DO THAT THEN WE DO NOT KNOWN WHAT WAS THE TYPE OF ORIGINAL OBJECT AND SERIALIZER WONT BE ABLE TO GET THE STRING BACK INTO THE CORRECT TYPE, //AS THE TYPE INFORMATION IS LOST if (propValueSerializer.CanSerializeToString(serializationManager, propertyValue) && propertyInfoType != typeof(object)) { using (new SafeXmlNodeWriter(serializationManager, obj, propertyObj, XmlNodeType.Attribute)) { //This is a work around to special case the markup extension serializer as it writes to the stream using writer if (propValueSerializer is MarkupExtensionSerializer) { propValueSerializer.SerializeToString(serializationManager, propertyValue); } else { string stringValue = propValueSerializer.SerializeToString(serializationManager, propertyValue); if (!string.IsNullOrEmpty(stringValue)) { stringValue = stringValue.Replace('\0', ' '); if (propertyValue is MarkupExtension || !(stringValue.StartsWith("{", StringComparison.Ordinal) && stringValue.EndsWith("}", StringComparison.Ordinal))) writer.WriteString(stringValue); else writer.WriteString("{}" + stringValue); } } } } else { complexProperties.Add(propertyObj); } } } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNoSerializeLogic, new object[] { propertyName, obj.GetType().FullName }), e)); } finally { Debug.Assert(serializationManager.Context.Current == propertyObj, "Serializer did not remove an object it pushed into stack."); serializationManager.Context.Pop(); } } try { serializer.OnBeforeSerializeContents(serializationManager, obj); } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); return; } // serialize compound properties as child elements of the current node. foreach (object propertyObj in complexProperties) { // get value and check for null string propertyName = String.Empty; object propertyValue = null; Type ownerType = null; bool isReadOnly = false; try { if (propertyObj is PropertyInfo) { PropertyInfo property = propertyObj as PropertyInfo; propertyName = property.Name; propertyValue = property.CanRead ? property.GetValue(obj, null) : null; ownerType = obj.GetType(); isReadOnly = (!property.CanWrite); } else if (propertyObj is DependencyProperty) { DependencyProperty dependencyProperty = propertyObj as DependencyProperty; propertyName = dependencyProperty.Name; propertyValue = dependencyProperties[dependencyProperty]; ownerType = dependencyProperty.OwnerType; isReadOnly = ((dependencyProperty.DefaultMetadata.Options & DependencyPropertyOptions.ReadOnly) == DependencyPropertyOptions.ReadOnly); } } catch (Exception e) { while (e is TargetInvocationException && e.InnerException != null) e = e.InnerException; serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerPropertyGetFailed, propertyName, ownerType.FullName, e.Message))); continue; } if (propertyObj is PropertyInfo && (PropertyInfo)propertyObj == contentProperty.Property) continue; if (propertyValue != null) { propertyValue = GetMarkupExtensionFromValue(propertyValue); WorkflowMarkupSerializer propValueSerializer = serializationManager.GetSerializer(propertyValue.GetType(), typeof(WorkflowMarkupSerializer)) as WorkflowMarkupSerializer; if (propValueSerializer != null) { using (new SafeXmlNodeWriter(serializationManager, obj, propertyObj, XmlNodeType.Element)) { if (isReadOnly) propValueSerializer.SerializeContents(serializationManager, propertyValue, writer, false); else propValueSerializer.SerializeObject(serializationManager, propertyValue, writer); } } else { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailableForSerialize, propertyValue.GetType().FullName))); } } } // serialize the contents try { object contents = contentProperty.GetContents(); if (contents != null) { contents = GetMarkupExtensionFromValue(contents); WorkflowMarkupSerializer propValueSerializer = serializationManager.GetSerializer(contents.GetType(), typeof(WorkflowMarkupSerializer)) as WorkflowMarkupSerializer; if (propValueSerializer == null) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailableForSerialize, contents.GetType()))); } else { // //NOTE: THE FOLLOWING CONDITION ABOUT contentProperty.Property.PropertyType != typeof(object) is VALID AS WE SHOULD NOT SERIALIZE A PROPERTY OF TYPE OBJECT TO STRING //IF WE DO THAT THEN WE DO NOT KNOWN WHAT WAS THE TYPE OF ORIGINAL OBJECT AND SERIALIZER WONT BE ABLE TO GET THE STRING BACK INTO THE CORRECT TYPE, //AS THE TYPE INFORMATION IS LOST if (propValueSerializer.CanSerializeToString(serializationManager, contents) && (contentProperty.Property == null || contentProperty.Property.PropertyType != typeof(object))) { string stringValue = propValueSerializer.SerializeToString(serializationManager, contents); if (!string.IsNullOrEmpty(stringValue)) { stringValue = stringValue.Replace('\0', ' '); if (contents is MarkupExtension || !(stringValue.StartsWith("{", StringComparison.Ordinal) && stringValue.EndsWith("}", StringComparison.Ordinal))) writer.WriteString(stringValue); else writer.WriteString("{}" + stringValue); } } else if (CollectionMarkupSerializer.IsValidCollectionType(contents.GetType())) { if (contentProperty.Property == null) { IEnumerable enumerableContents = contents as IEnumerable; foreach (object childObj in enumerableContents) { if (childObj == null) { SerializeObject(serializationManager, new NullExtension(), writer); } else { object childObj2 = childObj; bool dictionaryEntry = (childObj2 is DictionaryEntry); try { if (dictionaryEntry) { serializationManager.WorkflowMarkupStack.Push(childObj); childObj2 = ((DictionaryEntry)childObj2).Value; } childObj2 = GetMarkupExtensionFromValue(childObj2); WorkflowMarkupSerializer childObjectSerializer = serializationManager.GetSerializer(childObj2.GetType(), typeof(WorkflowMarkupSerializer)) as WorkflowMarkupSerializer; if (childObjectSerializer != null) childObjectSerializer.SerializeObject(serializationManager, childObj2, writer); else serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailableForSerialize, childObj2.GetType()))); } finally { if (dictionaryEntry) serializationManager.WorkflowMarkupStack.Pop(); } } } } else { propValueSerializer.SerializeContents(serializationManager, contents, writer, false); } } else { propValueSerializer.SerializeObject(serializationManager, contents, writer); } } } } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); return; } } try { serializer.OnAfterSerialize(serializationManager, obj); } catch (Exception e) { serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerThrewException, obj.GetType().FullName, e.Message), e)); return; } }