public void AddTypeNameAnnotationAsNeeded_DoesNotAddAnnotation_InDefaultMetadataMode()
        {
            // Arrange
            ODataComplexValue value = new ODataComplexValue();

            // Act
            ODataComplexTypeSerializer.AddTypeNameAnnotationAsNeeded(value, ODataMetadataLevel.Default);

            // Assert
            Assert.Null(value.GetAnnotation<SerializationTypeNameAnnotation>());
        }
        public void AddTypeNameAnnotationAsNeeded_AddsNullAnnotation_InJsonLightNoMetadataMode()
        {
            // Arrange
            string expectedTypeName = "TypeName";
            ODataComplexValue value = new ODataComplexValue
            {
                TypeName = expectedTypeName
            };

            // Act
            ODataComplexTypeSerializer.AddTypeNameAnnotationAsNeeded(value, ODataMetadataLevel.NoMetadata);

            // Assert
            SerializationTypeNameAnnotation annotation = value.GetAnnotation<SerializationTypeNameAnnotation>();
            Assert.NotNull(annotation); // Guard
            Assert.Null(annotation.TypeName);
        }
 internal void WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator)
 {
     this.IncreaseRecursionDepth();
     base.JsonWriter.StartObjectScope();
     string typeName = complexValue.TypeName;
     if (collectionValidator != null)
     {
         collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
     }
     IEdmComplexTypeReference type = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, propertyTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();
     if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0))
     {
         typeName = null;
     }
     SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>();
     if (annotation != null)
     {
         typeName = annotation.TypeName;
     }
     if (typeName != null)
     {
         base.JsonWriter.WriteName("__metadata");
         base.JsonWriter.StartObjectScope();
         base.JsonWriter.WriteName("type");
         base.JsonWriter.WriteValue(typeName);
         base.JsonWriter.EndObjectScope();
     }
     this.WriteProperties((type == null) ? null : type.ComplexDefinition(), complexValue.Properties, true, duplicatePropertyNamesChecker, null);
     base.JsonWriter.EndObjectScope();
     this.DecreaseRecursionDepth();
 }
        /// <summary>
        /// Writes out the value of a complex property.
        /// </summary>
        /// <param name="complexValue">The complex value to write.</param>
        /// <param name="propertyTypeReference">The metadata type for the complex value.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="collectionValidator">The collection validator instance to validate the type names and type kinds of collection items; null if no validation is needed.</param>
        /// <remarks>The current recursion depth should be a value, measured by the number of complex and collection values between
        /// this complex value and the top-level payload, not including this one.</remarks>
        internal void WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            this.IncreaseRecursionDepth();

            // Start the object scope which will represent the entire complex instance
            this.JsonWriter.StartObjectScope();

            // Write the "__metadata" : { "type": "typename" }
            // But only if we actually have a typename to write, otherwise we need the __metadata to be omitted entirely
            string typeName = complexValue.TypeName;

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
            }

            // resolve the type name to the type; if no type name is specified we will use the 
            // type inferred from metadata
            IEdmComplexTypeReference complexValueTypeReference =
                WriterValidationUtils.ResolveTypeNameForWriting(this.Model, propertyTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();

            // If the type is the same as the one specified by the parent collection, omit the type name, since it's not needed.
            if (typeName != null && collectionValidator != null)
            {
                string expectedItemTypeName = collectionValidator.ItemTypeNameFromCollection;
                if (string.CompareOrdinal(expectedItemTypeName, typeName) == 0)
                {
                    typeName = null;
                }
            }

            SerializationTypeNameAnnotation serializationTypeNameAnnotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>();
            if (serializationTypeNameAnnotation != null)
            {
                typeName = serializationTypeNameAnnotation.TypeName;
            }

            if (typeName != null)
            {
                // Write the __metadata object
                this.JsonWriter.WriteName(JsonConstants.ODataMetadataName);
                this.JsonWriter.StartObjectScope();

                // "type": "typename"
                this.JsonWriter.WriteName(JsonConstants.ODataMetadataTypeName);
                this.JsonWriter.WriteValue(typeName);

                // End the __metadata
                this.JsonWriter.EndObjectScope();
            }

            // Write the properties of the complex value as usual. Note we do not allow complex types to contain named stream properties.
            this.WriteProperties(
                complexValueTypeReference == null ? null : complexValueTypeReference.ComplexDefinition(),
                complexValue.Properties,
                true /* isComplexValue */,
                duplicatePropertyNamesChecker,
                null /*projectedProperties */);

            // End the object scope which represents the complex instance
            this.JsonWriter.EndObjectScope();

            this.DecreaseRecursionDepth();
        }
 internal bool WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties)
 {
     Action action2 = null;
     string typeName = complexValue.TypeName;
     if (collectionValidator != null)
     {
         collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
     }
     this.IncreaseRecursionDepth();
     IEdmComplexTypeReference reference = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();
     if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0))
     {
         typeName = null;
     }
     SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>();
     if (annotation != null)
     {
         typeName = annotation.TypeName;
     }
     Action beforePropertiesAction = beforeValueAction;
     if (typeName != null)
     {
         if (beforeValueAction != null)
         {
             if (action2 == null)
             {
                 action2 = delegate {
                     beforeValueAction();
                     this.WritePropertyTypeAttribute(typeName);
                 };
             }
             beforePropertiesAction = action2;
         }
         else
         {
             this.WritePropertyTypeAttribute(typeName);
         }
     }
     if (((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker))
     {
         IEdmComplexType definition = (IEdmComplexType) reference.Definition;
         if (base.Model.EpmCachedKeepPrimitiveInContent(definition) == null)
         {
             List<string> keptInContentPropertyNames = null;
             foreach (IEdmProperty property in from p in definition.Properties()
                 where p.Type.IsODataPrimitiveTypeKind()
                 select p)
             {
                 EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, property.Name);
                 if ((entityPropertyMapping != null) && entityPropertyMapping.KeepInContent)
                 {
                     if (keptInContentPropertyNames == null)
                     {
                         keptInContentPropertyNames = new List<string>();
                     }
                     keptInContentPropertyNames.Add(property.Name);
                 }
             }
             base.Model.SetAnnotationValue<CachedPrimitiveKeepInContentAnnotation>(definition, new CachedPrimitiveKeepInContentAnnotation(keptInContentPropertyNames));
         }
     }
     bool flag = this.WriteProperties((reference == null) ? null : reference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforePropertiesAction, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties);
     this.DecreaseRecursionDepth();
     return flag;
 }
        internal bool WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection,
            Action beforeValueAction,
            Action afterValueAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            string typeName = complexValue.TypeName;

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex);
            }

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the 
            // type inferred from metadata
            IEdmComplexTypeReference complexTypeReference =
                WriterValidationUtils.ResolveTypeNameForWriting(this.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull();

            // If the type is the same as the one specified by the parent collection, omit the type name, since it's not needed.
            if (typeName != null && collectionValidator != null)
            {
                string expectedItemTypeName = collectionValidator.ItemTypeNameFromCollection;
                if (string.CompareOrdinal(expectedItemTypeName, typeName) == 0)
                {
                    typeName = null;
                }
            }

            SerializationTypeNameAnnotation serializationTypeNameAnnotation = complexValue.GetAnnotation<SerializationTypeNameAnnotation>();
            if (serializationTypeNameAnnotation != null)
            {
                typeName = serializationTypeNameAnnotation.TypeName;
            }

            Action beforeValueCallbackWithTypeName = beforeValueAction;
            if (typeName != null)
            {
                // The beforeValueAction (if specified) will write the actual property element start.
                // So if we are to write the type attribute, we must postpone that after the start element was written.
                // And so we chain the existing action with our type attribute writing and use that
                // as the before action instead.
                if (beforeValueAction != null)
                {
                    beforeValueCallbackWithTypeName = () =>
                    {
                        beforeValueAction();
                        this.WritePropertyTypeAttribute(typeName);
                    };
                }
                else
                {
                    this.WritePropertyTypeAttribute(typeName);
                }
            }

            // NOTE: see the comments on ODataWriterBehavior.UseV1ProviderBehavior for more information
            // NOTE: We have to check for ProjectedPropertiesAnnotation.Empty here to avoid filling the cache for
            //       complex values we are writing only to ensure we don't have nested EPM-mapped null values 
            //       that will end up in the content eventually.
            if (this.MessageWriterSettings.WriterBehavior != null &&
                this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior &&
                !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker))
            {
                IEdmComplexType complexType = (IEdmComplexType)complexTypeReference.Definition;
                CachedPrimitiveKeepInContentAnnotation keepInContentCache = this.Model.EpmCachedKeepPrimitiveInContent(complexType);
                if (keepInContentCache == null)
                {
                    // we are about to write the first value of the given type; compute the keep-in-content information for the primitive properties of this type.
                    List<string> keepInContentPrimitiveProperties = null;

                    // initialize the cache with all primitive properties
                    foreach (IEdmProperty edmProperty in complexType.Properties().Where(p => p.Type.IsODataPrimitiveTypeKind()))
                    {
                        // figure out the keep-in-content value
                        EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, edmProperty.Name);
                        if (entityPropertyMapping != null && entityPropertyMapping.KeepInContent)
                        {
                            if (keepInContentPrimitiveProperties == null)
                            {
                                keepInContentPrimitiveProperties = new List<string>();
                            }

                            keepInContentPrimitiveProperties.Add(edmProperty.Name);
                        }
                    }

                    this.Model.SetAnnotationValue<CachedPrimitiveKeepInContentAnnotation>(complexType, new CachedPrimitiveKeepInContentAnnotation(keepInContentPrimitiveProperties));
                }
            }

            bool propertyWritten = this.WriteProperties(
                complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(),
                EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true),
                isWritingCollection,
                beforeValueCallbackWithTypeName,
                afterValueAction,
                duplicatePropertyNamesChecker,
                epmValueCache,
                epmSourcePathSegment,
                projectedProperties);

            this.DecreaseRecursionDepth();
            return propertyWritten;
        }