static object DeserializeToCSharpObject_Object_WithRecursion(IEnumerable <XNode> content, Type resultType, XElement parentElement, IReadOnlyList <Type> knownTypes, bool ignoreErrors, bool useXmlSerializerFormat)
        {
            // Quit if attempting to deserialize to an interface: //todo: investigate why this may happen.
            if (!resultType.IsInterface)
            {
                // Create the resulting class:
                object resultInstance = Activator.CreateInstance(resultType); //todo: replace with "System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type)" so that the type does not require a parameterless constructor.

                // Call the "OnDeserializing" method if any:
                CallOnDeserializingMethod(resultInstance, resultType);

                // Get the type information (namespace, etc.) by reading the DataContractAttribute and similar attributes, if present:
                TypeInformation typeInformation = DataContractSerializer_Helpers.GetTypeInformationByReadingAttributes(resultType, null);

                // Read the members of the target type:
                IEnumerable <MemberInformation> membersInformation = DataContractSerializer_Helpers.GetDataContractMembers(resultType, typeInformation.serializationType, useXmlSerializerFormat);

                // Make a dictionary of the members of the target type for faster lookup:
                Dictionary <string, MemberInformation> memberNameToMemberInformation = new Dictionary <string, MemberInformation>();
                foreach (var memberInformation in membersInformation)
                {
                    string memberName = memberInformation.Name;
                    if (resultType.FullName.StartsWith("System.Collections.Generic.KeyValuePair"))
                    {
                        if (memberName == "key")
                        {
                            memberName = "Key";
                        }
                        else if (memberName == "value")
                        {
                            memberName = "Value";
                        }
                    }
                    if (!memberNameToMemberInformation.ContainsKey(memberName))
                    {
                        memberNameToMemberInformation.Add(memberName, memberInformation);
                    }
                    else
                    {
                        MemberInformation collidingMemberInformation = memberNameToMemberInformation[memberName];
                        throw new InvalidDataContractException(
                                  string.Format(
                                      "Type '{0}' contains two members '{1}' 'and '{2}' with the same data member name '{3}'. Multiple members with the same name in one type are not supported. Consider changing one of the member names using DataMemberAttribute attribute.",
                                      resultType.ToString(),
                                      memberInformation.MemberInfo.Name,
                                      collidingMemberInformation.MemberInfo.Name,
                                      memberName
                                      ));
                    }
                }

                // Populate the values of the properties/members of the class:
                HashSet2 <string> membersForWhichWeSuccessfullSetTheValue = new HashSet2 <string>();
                foreach (XNode node in content)
                {
                    if (node is XElement) // Normally an object property was serialized as an XElement.
                    {
                        XElement xElement    = (XElement)node;
                        XName    elementName = xElement.Name;
                        string   elementNameWithoutNamespace = elementName.LocalName;

                        // Find the member that has the name of the XNode:
                        MemberInformation memberInformation;
                        if (memberNameToMemberInformation.TryGetValue(elementNameWithoutNamespace, out memberInformation))
                        {
                            // Avoid processing nodes that have the same name as other nodes already processed (this can happen in case of [XmlElement] attribute on enumerable members - cf. "special case" below - but it is handled differently):
                            if (!membersForWhichWeSuccessfullSetTheValue.Contains(memberInformation.Name))
                            {
                                object memberValue      = null;
                                Type   memberActualType = memberInformation.MemberType; // Note: this is the initial value. It may be modified below.
                                if (DataContractSerializer_Helpers.IsElementNil(xElement))
                                {
                                    //----------------------
                                    // XNode is "Nil", so we return the default value of the result type
                                    //----------------------

                                    memberValue = DataContractSerializer_Helpers.GetDefault(memberInformation.MemberType);
                                }
                                else
                                {
                                    bool isNull = false;

                                    //foreach (XAttribute attribute in xElement.Attributes(XNamespace.Get("http://www.w3.org/2001/XMLSchema-instance").GetName("nil"))) //doesn't work...
                                    //todo: try removing this foreach since it should be handled in the "if(IsElementDefaut(xElement))" above.
                                    foreach (XAttribute attribute in xElement.Attributes("nil")) //We have to do this here because those usually do not have nodes, which causes problems when doing the recursion.
                                    {
                                        isNull = Convert.ToBoolean(attribute.Value);
                                        if (isNull)
                                        {
                                            memberValue = null;
                                        }
                                    }

                                    if (!isNull)
                                    {
                                        memberActualType = DataContractSerializer_KnownTypes.GetCSharpTypeForNode(xElement, memberInformation.MemberInfo.DeclaringType, memberActualType, knownTypes, memberInformation);

                                        //if the type is nullable, we get the undelying type:
                                        Type nonNullableMemberType = memberActualType;
                                        if (memberActualType.FullName.StartsWith("System.Nullable`1"))
                                        {
                                            nonNullableMemberType = Nullable.GetUnderlyingType(memberActualType);
                                        }

                                        // Recursively create the value for the property:
                                        IEnumerable <XNode> propertyChildNodes = xElement.Nodes();

                                        //********** RECURSION **********
                                        memberValue = DeserializeToCSharpObject(propertyChildNodes, nonNullableMemberType, xElement, knownTypes, ignoreErrors, useXmlSerializerFormat);
                                    }

                                    //---------------------------------
                                    // Handle the special case where there is an [XmlElement] attribute on an enumerable member (XmlSerializer compatibility mode only):
                                    //
                                    // Example:
                                    //      <MyObject>
                                    //         <MyEnumerablePropertyName/>
                                    //         <MyEnumerablePropertyName/>
                                    //         <MyEnumerablePropertyName/>
                                    //      </MyObject>
                                    //
                                    // obtained via:
                                    //      class MyObject
                                    //      {
                                    //          [XmlElement]
                                    //          List<MyType> MyEnumerablePropertyName { get; set; }
                                    //      }
                                    //
                                    // cf. https://docs.microsoft.com/en-us/dotnet/standard/serialization/controlling-xml-serialization-using-attributes
                                    //---------------------------------
                                    Type itemsType = null;
                                    bool specialCaseWhereAnEnumerableHasTheXmlElementAttribute =
                                        (useXmlSerializerFormat &&
                                         memberInformation.HasXmlElementAttribute &&
                                         DataContractSerializer_Helpers.IsAssignableToGenericEnumerableOrArray(memberActualType, out itemsType));
                                    if (specialCaseWhereAnEnumerableHasTheXmlElementAttribute)
                                    {
                                        object deserializedEnumerable = DeserializeToCSharpObject_Enumerable_WithRecursion_SpecialCase(
                                            memberInformation.Name,
                                            content, memberActualType, knownTypes, ignoreErrors, itemsType, useXmlSerializerFormat);
                                        memberValue = deserializedEnumerable;
                                    }

                                    //---------------------------------
                                    // Set the value of the member:
                                    //---------------------------------

                                    DataContractSerializer_Helpers.SetMemberValue(resultInstance, memberInformation, memberValue);
                                    membersForWhichWeSuccessfullSetTheValue.Add(memberInformation.Name);
                                }
                            }
                        }
                        else
                        {
                            //-----------
                            // We ignore missing members, to mimic the behavior of the .NET DataContractSerializer.
                            //-----------
                            //throw new Exception("Member '" + memberName + "' not found in type '" + resultType.Name + "'.");
                        }
                    }
                }

                // In case of XmlSerializer compatibility mode, and [XmlAttribute] attribute on a class member, we also need to deserialize the XAttributes (cf. https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributeattribute(v=vs.110).aspx ):
                if (useXmlSerializerFormat)
                {
                    foreach (XAttribute attribute in parentElement.Attributes())
                    {
                        XName attributeName = attribute.Name;
                        // We assume that the object properties have no namespace //todo: fix this assumption, cf. "XmlAttributeAttribute.Namespace" for example (note: repetition of "Attribute" is intended)
                        if (string.IsNullOrEmpty(attributeName.NamespaceName))
                        {
                            string attributeNameWithoutNamespace = attributeName.LocalName;

                            // Find the member that has the name of the XAttribute:
                            MemberInformation memberInformation;
                            if (memberNameToMemberInformation.TryGetValue(attributeNameWithoutNamespace, out memberInformation) &&
                                memberInformation.HasXmlAttributeAttribute)
                            {
                                // Avoid processing members that have already been processed (just in case):
                                if (!membersForWhichWeSuccessfullSetTheValue.Contains(memberInformation.Name))
                                {
                                    string attributeValue = attribute.Value;

                                    // Check to see if the expected type is a value type:
                                    if (DataContractSerializer_ValueTypesHandler.TypesToNames.ContainsKey(memberInformation.MemberType))
                                    {
                                        // Attempt to deserialize the string:
                                        object memberValue = DataContractSerializer_ValueTypesHandler.ConvertStringToValueType(attributeValue, memberInformation.MemberType);

                                        // Set the value of the member:
                                        DataContractSerializer_Helpers.SetMemberValue(resultInstance, memberInformation, memberValue);
                                        membersForWhichWeSuccessfullSetTheValue.Add(memberInformation.Name);
                                    }
                                    else
                                    {
                                        //todo: report the error?
                                        if (memberInformation.MemberType == typeof(List <int>))
                                        {
                                            string[] splittedElements = attributeValue.Split(' ');

#if !BRIDGE
                                            List <int> listint = splittedElements.Select(Int32.Parse).ToList();
#else
                                            List <int> listint = new List <int>();

                                            foreach (string str in splittedElements)
                                            {
                                                listint.Add(Int32.Parse(str));
                                            }
#endif


                                            DataContractSerializer_Helpers.SetMemberValue(resultInstance, memberInformation, listint);
                                            membersForWhichWeSuccessfullSetTheValue.Add(memberInformation.Name);
                                        }
                                    }
                                }
                                else
                                {
                                    //todo: report the error?
                                }
                            }
                            else
                            {
                                //todo: report the error?
                            }
                        }
                    }
                }

                // Verify that the values of all the members marked as "IsRequired" have been set:
                foreach (var memberInformation in membersInformation)
                {
                    if (memberInformation.IsRequired &&
                        !membersForWhichWeSuccessfullSetTheValue.Contains(memberInformation.Name))
                    {
                        throw new SerializationException(string.Format("The member '{0}' is required but it was not found in the document being deserialized.", memberInformation.Name));
                    }
                }

                // Call the "OnDeserialized" method if any:
                CallOnDeserializedMethod(resultInstance, resultType);

                return(resultInstance);
            }
            else
            {
                return(null);
            }
        }
Exemplo n.º 2
0
 internal static TypeInformation GetTypeInformation(Type type)
 {
     TypeInformation typeInformation = null;
     object typeInformationObject = typeNameCache[type];
     if (typeInformationObject == null)
     {
         bool hasTypeForwardedFrom;
         string assemblyName = DataContract.GetClrAssemblyName(type, out hasTypeForwardedFrom);
         typeInformation = new TypeInformation(DataContract.GetClrTypeFullNameUsingTypeForwardedFromAttribute(type), assemblyName, hasTypeForwardedFrom);
         lock (typeNameCache)
         {
             typeNameCache[type] = typeInformation;
         }
     }
     else
     {
         typeInformation = (TypeInformation)typeInformationObject;
     }
     return typeInformation;
 }
Exemplo n.º 3
0
        //todo: those additional parameter are probably useless now.
        static List <XObject> SerializeToXObjects_ValueTypeOrString(object obj, Type objExpectedType, Type objectType, IReadOnlyList <Type> knownTypes, bool useXmlSerializerFormat, string nodeDefaultNamespaceIfAny, bool isRoot, bool isContainedInsideEnumerable, TypeInformation parentTypeInformation)
        {
            string str;

            if (obj is Double)
            {
#if SILVERLIGHT
                str = ((Double)obj).ToString(CultureInfo.InvariantCulture);
#else
                str = ((Double)obj).ToString();
#endif
            }
            else if (obj is char && objExpectedType != typeof(string)) //the second part is required because JSIL is bad at differentiating between char and string.
            {
                str = ((int)((char)obj)).ToString();                   //that looks really bad but it works...
            }
            else if (obj is DateTime)
            {
                DateTime dt = (DateTime)obj;
                str = INTERNAL_DateTimeHelpers.FromDateTime(dt);
            }
            else if (obj is bool)
            {
                str = obj.ToString().ToLower(); //this is required for WCF calls.
            }
            else
            {
                str = obj.ToString();
            }

            XText xtext = new XText(str);

            // If the value is the root or if it is inside an Enumerable, we add an XElement to surround it.
            // For example, if it is an integer 45, we surround 45 with <int xmlns="http://schemas.microsoft.com/2003/10/Serialization/">45</int>
            if (isRoot || isContainedInsideEnumerable)
            {
                // Determine the name of the type to use in the surrounding XElement:
                Type   objType = obj.GetType();
                string typeName;
                string typeNamespace;
                if (DataContractSerializer_ValueTypesHandler.TypesToNames.ContainsKey(objType))
                {
                    //-----------
                    // System built-in value type (int, bool, etc.)
                    //-----------
                    typeName      = DataContractSerializer_ValueTypesHandler.TypesToNames[objType];
                    typeNamespace = "http://schemas.microsoft.com/2003/10/Serialization/";
                }
                else
                {
                    //-----------
                    // User custom value type or enum
                    //-----------
                    typeName      = objType.Name;
                    typeNamespace = DataContractSerializer_Helpers.GetDefaultNamespace(objType.Namespace, useXmlSerializerFormat);
                }

                return(AddSurroundingXElement(xtext, typeName, typeNamespace, isRoot, isContainedInsideEnumerable));
            }
            else
            {
                return new List <XObject>()
                       {
                           xtext
                       }
            };
        }
Exemplo n.º 4
0
        /// <summary>
        /// Serializes the given object.
        /// </summary>
        /// <param name="obj">The instance of the object to serialize</param>
        /// <param name="objExpectedType">The type that the serialization should expect (it can be different from the type of the instance if the instance is from a child class of this type)</param>
        /// <param name="knownTypes">The types that should be known by the DataContractSerializer to allow serialization.</param>
        /// <param name="useXmlSerializerFormat">True if the DataContractSerializer should use the format normally used by the XmlSerializer.</param>
        /// <param name="isRoot">True if the current node we want to Serialize is the Root (it is the object that is put as the parameter when calling DataContractSerializer.SerializeToXXX.</param>
        /// <param name="isContainedInsideEnumerable">True if the current element is contained inside an Enumerable. It means that we should add a Node that contains the name of the class expected by the Enumerable (I think, this is old stuff for me)</param>
        /// <param name="parentTypeInformation">This contains informations on the type that contains the current object; or in the case of the root, on the type expected by the DataContractSerializer</param>
        /// <param name="nodeDefaultNamespaceIfAny">The default namespace that is applied on the node (not sure exactly since it is old stuff for me).</param>
        /// <returns>A List&lt;XNode&gt; of the nodes for the current element (a list in case of an Enumerable I think).</returns>
        internal static List <XObject> SerializeToXObjects(object obj, Type objExpectedType, IReadOnlyList <Type> knownTypes, bool useXmlSerializerFormat, bool isRoot, bool isContainedInsideEnumerable, TypeInformation parentTypeInformation, string nodeDefaultNamespaceIfAny)
        {
            //Note: objExpectedtype was added because we needed to know if obj was a string or a char. It is now also used for when the root is of a child class
            //      of the one expected by the DataContractSerializer. In that case, the DataContractSerializer should add a i:type="XXX" attribute to the root.

            //namespace for type (as in i:type): xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
            //when it's used: <Objet xmlns:d2p1="http://www.w3.org/2001/XMLSchema" i:type="d2p1:string">babab</Objet>

            Type objectType = obj.GetType();

            if (objectType.IsEnum || DataContractSerializer_ValueTypesHandler.TypesToNames.ContainsKey(objectType)) //todo: make sure this does not cause issues with the enum types compared to what is below commented.
            {
                //if (objectType.IsValueType || obj is string)
                //{
                //-------------------------
                // VALUE TYPE OR STRING (more or less a primitive type)
                //-------------------------

                return(SerializeToXObjects_ValueTypeOrString(obj, objExpectedType, objectType, knownTypes, useXmlSerializerFormat, nodeDefaultNamespaceIfAny, isRoot, isContainedInsideEnumerable, parentTypeInformation));
            }
            else if (obj.GetType() == typeof(Byte[]))
            {
                //-------------------------
                // BYTE ARRAY
                //-------------------------

                return(SerializeToXObjects_ByteArray(obj, isRoot, isContainedInsideEnumerable));
            }
            else if (obj is IEnumerable) //todo: should we be more precise and check if it implements IEnumerable<T> or is an Array, like we do during the Deserialization?
            {
                //-------------------------
                // ENUMERABLE (WITH RECURSION)
                //-------------------------

                return(SerializeToXObjects_Enumerable_WithRecursion(obj, objectType, knownTypes, useXmlSerializerFormat, nodeDefaultNamespaceIfAny, isRoot, isContainedInsideEnumerable));
            }
            else
            {
                //-------------------------
                // OTHER OBJECT TYPE (WITH RECURSION)
                //-------------------------

                return(SerializeToXObjects_Object_WithRecursion(obj, objectType, objExpectedType, knownTypes, useXmlSerializerFormat, nodeDefaultNamespaceIfAny, isRoot, isContainedInsideEnumerable, parentTypeInformation));
            }
        }
Exemplo n.º 5
0
        static List <XObject> SerializeToXObjects_Object_WithRecursion(object obj, Type objectType, Type resultExpectedType, IReadOnlyList <Type> knownTypes, bool useXmlSerializerFormat, string nodeDefaultNamespaceIfAny, bool isRoot, bool isContainedInsideEnumerable, TypeInformation parentTypeInformation)
        {
            // Call the "OnSerializing" method if any:
            CallOnSerializingMethod(obj, objectType);

            // Get the type information (namespace, etc.) by reading the DataContractAttribute and similar attributes, if present:
            TypeInformation typeInformation = DataContractSerializer_Helpers.GetTypeInformationByReadingAttributes(objectType, nodeDefaultNamespaceIfAny, useXmlSerializerFormat);

            // Process each member of the object:
            List <XObject> childrenObjects = new List <XObject>();
            IEnumerable <MemberInformationAndValue> membersAndValues = DataContractSerializer_Helpers.GetDataContractMembersAndValues(obj, serializationType: typeInformation.serializationType, useXmlSerializerFormat: useXmlSerializerFormat); //todo: normally, checking if a data contract is present is not enough to know if the type if "marked with attributes". According to the MSND article "Using Data Contracts", we should check for any of the following attributes: "DataContractAttribute, SerializableAttribute, CollectionDataContractAttribute, or EnumMemberAttribute attributes, or marked as serializable by any other means (such as IXmlSerializable)". cf. https://msdn.microsoft.com/en-us/library/ms733127(v=vs.100).aspx

            foreach (MemberInformationAndValue memberInfoAndValue in membersAndValues)
            {
                //------------------------------------
                // Process each member of the object
                //------------------------------------

                // Get the member information:
                MemberInfo memberInfo  = memberInfoAndValue.MemberInformation.MemberInfo;
                object     memberValue = memberInfoAndValue.MemberValue;
                string     memberName  = memberInfoAndValue.MemberInformation.Name;
                bool       hasXmlAttributeAttribute = memberInfoAndValue.MemberInformation.HasXmlAttributeAttribute;

                // If the member has the [XmlAttribute] attribute, we create an attribute for the member, otherwise we create an XNode:
                if (hasXmlAttributeAttribute && useXmlSerializerFormat)
                {
                    // Create the XAttribute for the member:
                    if (memberValue != null)
                    {
                        string memberValueAsString = memberValue.ToString();
                        XName  xnameForMember      = GetXNameForMember(objectType, typeInformation, ref memberName);
                        var    xAtttribute         = new XAttribute(xnameForMember, memberValueAsString);
                        childrenObjects.Add(xAtttribute);
                    }
                }
                else
                {
                    // Create the XNode for the member:
                    XName    xnameForMember    = GetXNameForMember(objectType, typeInformation, ref memberName);
                    XElement xElementForMember = new XElement(xnameForMember);

                    bool isNull = ((memberValue == null) || DataContractSerializer_Helpers.CheckIfObjectIsNullNullable(memberValue));

                    if (!isNull)
                    {
                        Type expectedType = memberInfoAndValue.MemberInformation.MemberType;
                        Type memberType   = memberValue.GetType();

                        // Work around JSIL issues:
                        if (memberValue is char && expectedType == typeof(char)) //Note: this test is required because JSIL thinks that object testobj = 'c'; testobj.GetType() should return System.String...
                        {
                            memberType = typeof(char);
                        }
                        else if (expectedType == typeof(double)) //Note: this test is required because, with JSIL, "GetType" on a double returns "int" instead of "double". //todo: see if other workarounds to JSIL bugs like this one are required.
                        {
                            memberType = typeof(double);
                        }
                        else if (expectedType == typeof(byte)) //same as above.
                        {
                            memberType = typeof(byte);
                        }
                        else if (expectedType == typeof(float)) //same as above.
                        {
                            memberType = typeof(float);
                        }

                        bool isValueEnumerableDifferentThanString = (memberValue is IEnumerable) && !(memberValue is string);

                        Type nonNullableMemberType = memberType;
                        if (memberType.FullName.StartsWith("System.Nullable`1"))
                        {
                            nonNullableMemberType = Nullable.GetUnderlyingType(memberType);
                        }
                        Type nonNullableExpectedType = expectedType;
                        if (expectedType.FullName.StartsWith("System.Nullable`1"))
                        {
                            nonNullableExpectedType = Nullable.GetUnderlyingType(expectedType);
                        }

                        if (nonNullableMemberType != nonNullableExpectedType && !isValueEnumerableDifferentThanString)
                        {
                            //we want to add a type attribute to be able to know when deserializing that this is another type that the property's
                            if (DataContractSerializer_ValueTypesHandler.TypesToNames.ContainsKey(nonNullableMemberType)) //todo: should we add the nullable versions?
                            {
                                string prefixForType = xElementForMember.GetPrefixOfNamespace("http://www.w3.org/2001/XMLSchema");
                                if (string.IsNullOrWhiteSpace(prefixForType))
                                {
                                    //we need to create a prefix for that
                                    //todo:see if it would be ok to use d2p1 like silverlight seems to do, and if so, replace the following with just that.

                                    prefixForType = DataContractSerializer_Helpers.GenerateUniqueNamespacePrefixIfNeeded(xElementForMember, "http://www.w3.org/2001/XMLSchema");

                                    //we can finally add the type attribute:
                                    xElementForMember.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("type"), prefixForType + ":" + DataContractSerializer_ValueTypesHandler.TypesToNames[nonNullableMemberType]));
                                }
                                else
                                {
                                    xElementForMember.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("type"), prefixForType + ":" + DataContractSerializer_ValueTypesHandler.TypesToNames[nonNullableMemberType]));
                                }
                            }
                            else
                            {
                                bool isTypeOk = DataContractSerializer_KnownTypes.CheckIfItIsAKnownType(obj, objectType, knownTypes, memberType);
                                if (isTypeOk)
                                {
                                    if (memberType.IsEnum) //enums are a special case because JSIL is not capable of handling Custom attributes on them.
                                    {
                                        xElementForMember.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("type"), memberType.Name));
                                    }
                                    else
                                    {
                                        string defaultNamespace = DataContractSerializer_Helpers.GetDefaultNamespace(memberType.Namespace, useXmlSerializerFormat);

                                        // Get the type information (namespace, etc.) by reading the DataContractAttribute and similar attributes, if present:
                                        TypeInformation childTypeInformation = DataContractSerializer_Helpers.GetTypeInformationByReadingAttributes(memberType, defaultNamespace, useXmlSerializerFormat);

                                        string prefixForTypeName = "";
                                        if (childTypeInformation.NamespaceName != null)                                                                                                            //when the namespaceName is null I guess it means we don't need something like type="sth:ChildTypeName" but type="ChildTypeName" is sufficient.
                                        {
                                            prefixForTypeName = DataContractSerializer_Helpers.GenerateUniqueNamespacePrefixIfNeeded(xElementForMember, childTypeInformation.NamespaceName) + ":"; // note: we added : directly because whenever we have a prefix, we have ':' right after.
                                        }

                                        xElementForMember.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("type"), prefixForTypeName + childTypeInformation.Name));
                                    }
                                }
                                else
                                {
                                    string namespaceNameToPutInException = "";
                                    if (typeInformation.NamespaceName != null)
                                    {
                                        namespaceNameToPutInException = ":" + typeInformation.NamespaceName;
                                    }
                                    throw new SerializationException("Type \"" + memberType.FullName + "\" with data contract name \"" + memberType.Name + namespaceNameToPutInException + "\" is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.");
                                }
                            }
                        }

                        //********** RECURSION **********
                        List <XObject> xobjectsForMemberValue = SerializeToXObjects(memberValue, memberType, knownTypes, useXmlSerializerFormat, isRoot: false, isContainedInsideEnumerable: false, parentTypeInformation: typeInformation, nodeDefaultNamespaceIfAny: typeInformation.NamespaceName);
                        foreach (XObject xobjectForMemberValue in xobjectsForMemberValue) // Note: the collection usually contains only 1 node, but there may be multiple nodes if for example we are serializing an Enumerable.
                        {
                            xElementForMember.Add(xobjectForMemberValue);
                        }
                    }
                    else
                    {
                        //------------------------------------
                        // The value of the member is "null"
                        //------------------------------------

                        XName nilName = XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("nil");
                        xElementForMember.Add(new XAttribute(nilName, "true"));
                    }
                    childrenObjects.Add(xElementForMember);
                }
            }
            if (isRoot || isContainedInsideEnumerable)
            {
                //we add a XElement with the type name as its name.
                //we create the XName for the node containing the type.

                //----------------------------------
                // Determine the name of the object to use in the XML:
                //----------------------------------

                //we get the type expected by the parent Enumerable:
                Type typeExpectedByParentEnumerable = isRoot ? resultExpectedType : DataContractSerializer_Helpers.GetInterface(parentTypeInformation.Type, "IEnumerable`1").GetGenericArguments()[0];

                string objectTypeName;

                // If the parent of the object is a collection that has the
                // "CollectionDataContractAttribute", we use the name specified
                // by that attribute, if any. Otherwise we determine the name
                // from the Type of the object

                if (parentTypeInformation != null &&
                    !string.IsNullOrEmpty(parentTypeInformation.ItemName))
                {
                    objectTypeName = parentTypeInformation.ItemName;
                }
                else
                {
                    objectTypeName = typeInformation.Name;
                }

                XName elementName;
                if (typeInformation.NamespaceName == null)
                {
                    elementName = objectTypeName;
                }
                else
                {
                    elementName = XNamespace.Get(typeInformation.NamespaceName).GetName(objectTypeName);
                }

                XElement xelement = new XElement(elementName);

                if (objectType.IsEnum) //enums are a special case because JSIL is not capable of handling Custom attributes on them.
                {
                    xelement.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("type"), objectTypeName));
                }
                else if (typeInformation.NamespaceName != null && !objectType.IsGenericType && typeExpectedByParentEnumerable != obj.GetType())
                {
                    string prefixForTypeName = DataContractSerializer_Helpers.GenerateUniqueNamespacePrefixIfNeeded(xelement, typeInformation.NamespaceName);

                    xelement.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("type"), prefixForTypeName + ":" + typeInformation.Name));
                }

                foreach (XObject child in childrenObjects)
                {
                    xelement.Add(child);
                }
                childrenObjects = new List <XObject>()
                {
                    xelement
                };
            }

            // Call the "OnSerialized" method if any:
            CallOnSerializedMethod(obj, objectType);

            return(childrenObjects);
        }
Exemplo n.º 6
0
        static List <XObject> SerializeToXObjects_Enumerable_WithRecursion(object obj, Type objectType, IReadOnlyList <Type> knownTypes, bool useXmlSerializerFormat, string nodeDefaultNamespaceIfAny, bool isRoot, bool isContainedInsideEnumerable)
        {
            List <XObject> result = new List <XObject>();

            // Get the type information (namespace, etc.) by reading the DataContractAttribute and similar attributes, if present:
            TypeInformation typeInformation = DataContractSerializer_Helpers.GetTypeInformationByReadingAttributes(objectType, nodeDefaultNamespaceIfAny, useXmlSerializerFormat);

            // Traverse the collection:
            foreach (object item in (IEnumerable)obj)
            {
                //todo: USEMETHODTOGETTYPE: make a method that returns the actual type of object and replace all USEMETHODTOGETTYPE with a call to this method. (also find other places where we could use it)
                Type itemType = item.GetType();

                if (item is char) //special case because JSIL thinks a variable of type object containing a char contains a string.
                {
                    itemType = typeof(char);
                }

                //********** RECURSION **********
                //WILD GUESS: Since we are going to the content of an IEnumerable, the namespace of the IEnumerable becomes irrelevant for its content so we give them null instead.
                List <XObject> xnodesForItem = SerializeToXObjects(item, itemType, knownTypes, useXmlSerializerFormat, isRoot: false, isContainedInsideEnumerable: true, parentTypeInformation: typeInformation, nodeDefaultNamespaceIfAny: typeInformation.NamespaceName);

                // Keep only the first node:
                if (xnodesForItem.Count > 1)
                {
                    throw new Exception("When serializing an IEnumerable, we do not expect an item of the enumerable to be serialized as multiple XNodes.");
                }
                XObject nodeForItem = xnodesForItem.First();

                // If it's a value type, add an XElement to surround the node:
                if (nodeForItem is XText)
                {
                    //todo: do we still ever enter this block? In fact, we now add the surrounding XElement in other places.

                    //------------
                    // Value type
                    //------------

                    //we assume namespace of the element is "http://schemas.microsoft.com/2003/10/Serialization/Arrays" as that is what it is for both an array and a list of string
                    //the name of the element should be found in the Dictionary Type --> XName with the item's type
                    //Note: We shoud not arrive here if we cannot find the name in the Dictionary mentioned (except maybe for structs but we'll see when we'll deal with them).
                    XName    elementName = XNamespace.Get("http://schemas.microsoft.com/2003/10/Serialization/Arrays").GetName(DataContractSerializer_ValueTypesHandler.TypesToNames[item.GetType()]); //in this line, we create a XName with the namespace that seems to be used for all Enumerables and a name associated with the type.
                    XElement element     = new XElement(elementName);
                    //  name of the element: use the Dictionary Type --> XName with the item's type
                    element.Add(nodeForItem);
                    nodeForItem = element;
                }

                // Add the node to the resulting collection of nodes:
                result.Add(nodeForItem);
            }

            // If the value is the root, add an XElement "ArrayOf..." to surround the nodes:
            if (isRoot)
            {
                Type itemsType;
                if (DataContractSerializer_Helpers.IsAssignableToGenericEnumerableOrArray(objectType, out itemsType))
                {
                    string elementName = "Array";

                    if (DataContractSerializer_ValueTypesHandler.TypesToNames.ContainsKey(itemsType))
                    {
                        elementName = "ArrayOf" + DataContractSerializer_ValueTypesHandler.TypesToNames[itemsType];
                    }
                    else
                    {
                        // In case of nested types, replace the '+' with '.', and do other changes to obtain the type name to use in the serialization:
                        string itemsTypeName = DataContractSerializer_Helpers.GetTypeNameSafeForSerialization(itemsType);

                        elementName = "ArrayOf" + itemsTypeName;
                    }
                    XElement xElement = new XElement(XNamespace.Get("http://schemas.microsoft.com/2003/10/Serialization/Arrays").GetName(elementName), result);
                    xElement.Add(new XAttribute(XNamespace.Get(DataContractSerializer_Helpers.XMLNS_NAMESPACE).GetName("xmlns:i"), DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE));
                    return(new List <XObject>()
                    {
                        xElement
                    });
                }
                else
                {
                    throw new SerializationException(string.Format("The type '{0}' cannot be serialized because it does implement IEnumerable<T> nor is it an Array.", objectType.ToString())); //todo: see if we can avoid this limitation.
                }
            }

            return(result);
        }
 internal static TypeInformation GetTypeInformation(Type type)
 {
     TypeInformation information = null;
     object obj2 = typeNameCache[type];
     if (obj2 == null)
     {
         information = new TypeInformation(DataContract.GetClrTypeFullNameUsingTypeForwardedFromAttribute(type), DataContract.GetClrAssemblyName(type));
         lock (typeNameCache)
         {
             typeNameCache[type] = information;
             return information;
         }
     }
     return (TypeInformation) obj2;
 }