Пример #1
0
 public TypeInformation(Type type, string name, string namespaceName, string itemName, string keyName, string valueName, SerializationType isAttributeSuchAsDataContractPresent)
 {
     this.Type              = type;
     this.Name              = DataContractSerializer_Helpers.GetTypeNameSafeForSerialization(type);
     this.NamespaceName     = namespaceName;
     this.ItemName          = itemName;
     this.KeyName           = keyName;
     this.ValueName         = valueName;
     this.serializationType = isAttributeSuchAsDataContractPresent;
 }
        static bool IsTypeSameAsTheOneSpecifiedInXmlTypeAttribute(Type typeToCompare, string typeNameInXmlTypeAttribute, string typeNamespaceInXmlTypeAttribute)
        {
            string typeName = DataContractSerializer_Helpers.GetTypeNameSafeForSerialization(typeToCompare);

            string typeNamespaceName = DataContractSerializer_Helpers.DATACONTRACTSERIALIZER_OBJECT_DEFAULT_NAMESPACE + typeToCompare.Namespace;
            //todo: verify that the namespace is OK or if we should get the namespace specified by the DataContract attribute by calling "GetTypeInformationByReadingAttributes".
            TypeInformation referenceTypeInfo = DataContractSerializer_Helpers.GetTypeInformationByReadingAttributes(typeToCompare, null);

            typeNamespaceName = referenceTypeInfo.NamespaceName;
            typeName          = referenceTypeInfo.Name;

            // Compare the KnownType with the type name and namespace that we are looking for:
            if (typeName == typeNameInXmlTypeAttribute &&
                typeNamespaceName == typeNamespaceInXmlTypeAttribute)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Пример #3
0
        static List <XNode> SerializeToXNodes_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);

            // Process each member of the object:
            List <XNode> childrenNodes = new List <XNode>();
            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;

                // Create the XNode for the member:
                XName xnameForMember;
                if (objectType.FullName.StartsWith("System.Collections.Generic.KeyValuePair"))
                {
                    if (memberName == "key")
                    {
                        memberName = "Key";
                    }
                    else if (memberName == "value")
                    {
                        memberName = "Value";
                    }
                    xnameForMember = memberName;
                }
                else
                {
                    if (typeInformation.NamespaceName == null) //todo: make sure we want to use this typeInformation since it comes from the type that contains of the current member and not the member itself.
                    {
                        xnameForMember = memberName;
                    }
                    else
                    {
                        xnameForMember = XNamespace.Get(typeInformation.NamespaceName).GetName(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.DATACONTRACTSERIALIZER_OBJECT_DEFAULT_NAMESPACE + memberType.Namespace;

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

                                    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 <XNode> xnodesForMemberValue = SerializeToXNodes(memberValue, memberType, knownTypes, useXmlSerializerFormat, isRoot: false, isContainedInsideEnumerable: false, parentTypeInformation: typeInformation, nodeDefaultNamespaceIfAny: typeInformation.NamespaceName);
                    foreach (XNode xnodeForMemberValue in xnodesForMemberValue) // Note: the collection usually contains only 1 node, but there may be multiple nodes if for example we are serializing an Enumerable.
                    {
                        xElementForMember.Add(xnodeForMemberValue);
                    }
                }
                else
                {
                    //------------------------------------
                    // The value of the member is "null"
                    //------------------------------------

                    XName nilName = XNamespace.Get(DataContractSerializer_Helpers.XMLSCHEMA_NAMESPACE).GetName("nil");
                    xElementForMember.Add(new XAttribute(nilName, "true"));
                }
                childrenNodes.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 = DataContractSerializer_Helpers.GetTypeNameSafeForSerialization(typeExpectedByParentEnumerable); // Note: in case of nested types, this method replaces the '+' with '.', and does other changes to obtain the type name to use in the serialization.
                }

                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 (XNode node in childrenNodes)
                {
                    xelement.Add(node);
                }
                childrenNodes = new List <XNode>()
                {
                    xelement
                };
            }

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

            return(childrenNodes);
        }
Пример #4
0
        static List <XNode> SerializeToXNodes_Enumerable_WithRecursion(object obj, Type objectType, IReadOnlyList <Type> knownTypes, bool useXmlSerializerFormat, string nodeDefaultNamespaceIfAny, bool isRoot, bool isContainedInsideEnumerable)
        {
            List <XNode> result = new List <XNode>();

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

            // 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 <XNode> xnodesForItem = SerializeToXNodes(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.");
                }
                XNode 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 <XNode>()
                    {
                        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);
        }