Example #1
0
        /// <summary>
        /// Creates a new instance of MaterializerEntry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="format">The format the entry was read in.</param>
        /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param>
        /// <param name="model">The client model.</param>
        private MaterializerEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model)
        {
            Debug.Assert(entry != null, "entry != null");

            this.entry            = entry;
            this.Format           = format;
            this.entityDescriptor = new EntityDescriptor(model);

            this.isAtomOrTracking = isTracking || this.Format == ODataFormat.Atom;

            string serverTypeName = this.Entry.TypeName;
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();

            if (serializationTypeNameAnnotation != null)
            {
                // If the annotation has a value use it. Otherwise, in JSON-Light, the types can be inferred from the
                // metadata URI even if they are not present on the wire, so just use the type name from the entry.
                if (serializationTypeNameAnnotation.TypeName != null || this.Format != ODataFormat.Json)
                {
                    serverTypeName = serializationTypeNameAnnotation.TypeName;
                }
            }

            this.entityDescriptor.ServerTypeName = serverTypeName;
        }
Example #2
0
        private ResourceType GetEntryResourceType(ODataEntry entry, ResourceType expectedType)
        {
            ResourceType type;
            string       typeName = entry.TypeName;
            SerializationTypeNameAnnotation annotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();

            if (annotation != null)
            {
                typeName = annotation.TypeName;
            }
            if (string.IsNullOrEmpty(typeName))
            {
                type = expectedType;
                if (base.Service.Provider.HasDerivedTypes(type))
                {
                    throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance);
                }
                return(type);
            }
            type = base.Service.Provider.TryResolveResourceType(typeName);
            if (type == null)
            {
                throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_InvalidTypeName(typeName));
            }
            if (!expectedType.IsAssignableFrom(type))
            {
                throw DataServiceException.CreateBadRequestError(System.Data.Services.Strings.BadRequest_InvalidTypeSpecified(typeName, expectedType.FullName));
            }
            return(type);
        }
Example #3
0
        private ODataComplexValue CreateODataComplexValue(Type complexType, object value, string propertyName, bool isCollectionItem, List <object> visitedComplexTypeObjects)
        {
            ClientTypeAnnotation clientTypeAnnotation = ClientEdmModel.GetModel(this.requestInfo.MaxProtocolVersion).GetClientTypeAnnotation(complexType);

            if (visitedComplexTypeObjects == null)
            {
                visitedComplexTypeObjects = new List <object>();
            }
            else if (visitedComplexTypeObjects.Contains(value))
            {
                if (propertyName != null)
                {
                    throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName));
                }
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Serializer_LoopsNotAllowedInNonPropertyComplexTypes(clientTypeAnnotation.ElementTypeName));
            }
            if (value == null)
            {
                return(null);
            }
            visitedComplexTypeObjects.Add(value);
            ODataComplexValue value2 = new ODataComplexValue();

            if (!isCollectionItem)
            {
                SerializationTypeNameAnnotation annotation = new SerializationTypeNameAnnotation {
                    TypeName = this.requestInfo.GetServerTypeName(clientTypeAnnotation)
                };
                value2.SetAnnotation <SerializationTypeNameAnnotation>(annotation);
            }
            value2.Properties = this.PopulateProperties(clientTypeAnnotation, value, visitedComplexTypeObjects);
            visitedComplexTypeObjects.Remove(value);
            return(value2);
        }
        /// <summary>
        /// Determines the type name to write to the payload.  Json Light type names are only written into the payload for open properties
        /// or if the payload type name is more derived than the model type name.
        /// </summary>
        /// <param name="value">The ODataValue whose type name is to be written.</param>
        /// <param name="typeReferenceFromMetadata">The type as expected by the model.</param>
        /// <param name="typeReferenceFromValue">The type resolved from the value.</param>
        /// <param name="isOpenProperty">true if the type name belongs to an open property, false otherwise.</param>
        /// <returns>Type name to write to the payload, or null if no type should be written.</returns>
        internal override string GetValueTypeNameForWriting(
            ODataValue value,
            IEdmTypeReference typeReferenceFromMetadata,
            IEdmTypeReference typeReferenceFromValue,
            bool isOpenProperty)
        {
            DebugUtils.CheckNoExternalCallers();

            SerializationTypeNameAnnotation typeNameAnnotation = value.GetAnnotation <SerializationTypeNameAnnotation>();

            if (typeNameAnnotation != null)
            {
                return(typeNameAnnotation.TypeName);
            }

            // Do not write type name when the type is native json type.
            if (typeReferenceFromValue != null &&
                typeReferenceFromValue.IsPrimitive() &&
                JsonSharedUtils.ValueTypeMatchesJsonType((ODataPrimitiveValue)value, typeReferenceFromValue.AsPrimitive()))
            {
                return(null);
            }

            return(GetTypeNameFromValue(value));
        }
        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();
        }
Example #6
0
        /// <summary>
        /// Converts the object to ODataEntry or ODataEntityReferenceLink.
        /// </summary>
        /// <param name="value">The value of the <see cref="UriOperationParameter"/>.</param>
        /// <param name="elementType">The type of the value</param>
        /// <param name="useEntityReference">If true, use entity reference, instead of entity to serialize the parameter.</param>
        /// <returns>The converted result.</returns>
        private object ConvertToEntityValue(object value, Type elementType, bool useEntityReference)
        {
            object valueInODataFormat;

            if (!useEntityReference)
            {
                valueInODataFormat = this.propertyConverter.CreateODataEntry(elementType, value);

                ODataEntry entry = (ODataEntry)valueInODataFormat;
                SerializationTypeNameAnnotation serializedTypeNameAnnotation =
                    entry.GetAnnotation <SerializationTypeNameAnnotation>();
                if (serializedTypeNameAnnotation == null ||
                    string.IsNullOrEmpty(serializedTypeNameAnnotation.TypeName))
                {
                    throw Error.InvalidOperation(Strings.DataServiceException_GeneralError);
                }
            }
            else
            {
                EntityDescriptor resource = this.requestInfo.EntityTracker.GetEntityDescriptor(value);
                Uri link = resource.GetLatestIdentity();
                valueInODataFormat = new ODataEntityReferenceLink()
                {
                    Url = link,
                };
            }

            return(valueInODataFormat);
        }
Example #7
0
        private MaterializerEntry(ODataEntry entry, DataServiceProtocolVersion maxProtocolVersion)
        {
            this.entry            = entry;
            this.entityDescriptor = new System.Data.Services.Client.EntityDescriptor(maxProtocolVersion);
            SerializationTypeNameAnnotation annotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();

            this.entityDescriptor.ServerTypeName = (annotation != null) ? annotation.TypeName : (this.entityDescriptor.ServerTypeName = this.Entry.TypeName);
        }
        internal void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType)
        {
            this.IncreaseRecursionDepth();
            base.JsonWriter.StartObjectScope();
            string typeName = collectionValue.TypeName;
            IEdmCollectionTypeReference type  = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType);
            string itemTypeNameFromCollection = null;

            if (typeName != null)
            {
                itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName);
            }
            SerializationTypeNameAnnotation annotation = collectionValue.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();
            }
            base.JsonWriter.WriteDataArrayName();
            base.JsonWriter.StartArrayScope();
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                IEdmTypeReference propertyTypeReference = (type == null) ? null : type.ElementType();
                CollectionWithoutExpectedTypeValidator collectionValidator           = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection);
                DuplicatePropertyNamesChecker          duplicatePropertyNamesChecker = null;
                foreach (object obj2 in items)
                {
                    ValidationUtils.ValidateCollectionItem(obj2, false);
                    ODataComplexValue complexValue = obj2 as ODataComplexValue;
                    if (complexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker();
                        }
                        this.WriteComplexValue(complexValue, propertyTypeReference, false, duplicatePropertyNamesChecker, collectionValidator);
                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        this.WritePrimitiveValue(obj2, collectionValidator, propertyTypeReference);
                    }
                }
            }
            base.JsonWriter.EndArrayScope();
            base.JsonWriter.EndObjectScope();
            this.DecreaseRecursionDepth();
        }
Example #9
0
            /// <summary>
            /// Converts the SerializationTypeNameAnnotation from the <paramref name="odataAnnotatable"/> into a SerializationTypeNameTestAnnotation
            /// added to the <paramref name="payloadElement"/>.
            /// </summary>
            /// <param name="odataAnnotatable">The OData OM value to get the annotation from.</param>
            /// <param name="payloadElement">The payload element to add the converted annotation to.</param>
            private void ConvertSerializationTypeNameAnnotation(ODataAnnotatable odataAnnotatable, ODataPayloadElement payloadElement)
            {
                SerializationTypeNameAnnotation serializationTypeNameAnnotation = odataAnnotatable.GetAnnotation <SerializationTypeNameAnnotation>();

                if (serializationTypeNameAnnotation != null)
                {
                    payloadElement.AddAnnotation(new SerializationTypeNameTestAnnotation {
                        TypeName = serializationTypeNameAnnotation.TypeName
                    });
                }
            }
Example #10
0
        /// <summary>
        /// Determines the entity type name to write to the payload.
        /// </summary>
        /// <param name="expectedTypeName">The expected type name, e.g. the base type of the set or the nav prop.</param>
        /// <param name="entry">The ODataEntry whose type is to be written.</param>
        /// <returns>Type name to write to the payload, or null if no type name should be written.</returns>
        internal override string GetEntryTypeNameForWriting(string expectedTypeName, ODataEntry entry)
        {
            Debug.Assert(entry != null, "entry != null");

            SerializationTypeNameAnnotation typeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();

            if (typeNameAnnotation != null)
            {
                return(typeNameAnnotation.TypeName);
            }

            return(entry.TypeName);
        }
Example #11
0
        private void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, bool isWritingCollection)
        {
            this.IncreaseRecursionDepth();
            string typeName = collectionValue.TypeName;
            IEdmCollectionTypeReference type  = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(base.Model, propertyTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType);
            string itemTypeNameFromCollection = null;

            if (typeName != null)
            {
                itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName);
            }
            SerializationTypeNameAnnotation annotation = collectionValue.GetAnnotation <SerializationTypeNameAnnotation>();

            if (annotation != null)
            {
                typeName = annotation.TypeName;
            }
            if (typeName != null)
            {
                this.WritePropertyTypeAttribute(typeName);
            }
            IEdmTypeReference metadataTypeReference = (type == null) ? null : type.ElementType();
            CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection);
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object obj2 in items)
                {
                    ValidationUtils.ValidateCollectionItem(obj2, false);
                    base.XmlWriter.WriteStartElement("d", "element", base.MessageWriterSettings.WriterBehavior.ODataNamespace);
                    ODataComplexValue complexValue = obj2 as ODataComplexValue;
                    if (complexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker();
                        }
                        this.WriteComplexValue(complexValue, metadataTypeReference, false, isWritingCollection, null, null, duplicatePropertyNamesChecker, collectionValidator, null, null, null);
                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        this.WritePrimitiveValue(obj2, collectionValidator, metadataTypeReference);
                    }
                    base.XmlWriter.WriteEndElement();
                }
            }
            this.DecreaseRecursionDepth();
        }
Example #12
0
        public void TypeNameShouldComeFromSerializationTypeNameAnnotationForPrimitiveValue()
        {
            var stna = new SerializationTypeNameAnnotation()
            {
                TypeName = "FromSTNA"
            };
            var value = new ODataPrimitiveValue(42);

            value.SetAnnotation(stna);
            this.typeNameOracle.GetValueTypeNameForWriting(value,
                                                           EdmCoreModel.Instance.GetInt32(true),
                                                           EdmCoreModel.Instance.GetInt32(false),
                                                           /* isOpenProperty*/ false).Should().Be("FromSTNA");
        }
        public void AddTypeNameAnnotationAsNeeded_AddsAnnotation_InJsonLightMetadataMode()
        {
            // Arrange
            ODataPrimitiveValue primitive = new ODataPrimitiveValue((short)1);

            // Act
            ODataPrimitiveSerializer.AddTypeNameAnnotationAsNeeded(primitive, ODataMetadataLevel.FullMetadata);

            // Assert
            SerializationTypeNameAnnotation annotation = primitive.GetAnnotation <SerializationTypeNameAnnotation>();

            Assert.NotNull(annotation); // Guard
            Assert.Equal("Edm.Int16", annotation.TypeName);
        }
Example #14
0
        private ResourceType GetEntryResourceType(ODataEntry entry, ResourceType expectedType)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(expectedType != null, "We must always have an expected type for entities.");
            Debug.Assert(expectedType.ResourceTypeKind == ResourceTypeKind.EntityType, "Expected type for entities must be an entity type.");

            ResourceType resourceType;

            // Note that we can't rely on the ODataLib handling in this case completely.
            // In the WCF DS Server mode the ODataLib uses the lax validation, which means that it doesn't actually
            // validate almost anything (to follow the same logic as used for complex types).
            // So we have to implement the validation logic here on top of ODataLib.
            // We also can't use the type name reported by the entry property always, as that is the resolved type name.
            // It's safer to use the actual type name from the payload if it's available.
            string payloadTypeName = entry.TypeName;
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();

            if (serializationTypeNameAnnotation != null)
            {
                payloadTypeName = serializationTypeNameAnnotation.TypeName;
            }

            // If the type is not specified in the payload, we assume the type to be the expected type.
            if (String.IsNullOrEmpty(payloadTypeName))
            {
                // Check if the expected type takes part in inheritance.
                resourceType = expectedType;
                if (this.Service.Provider.HasDerivedTypes(resourceType))
                {
                    throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance);
                }
            }
            else
            {
                // Otherwise, try and resolve the name specified in the payload.
                resourceType = this.Service.Provider.TryResolveResourceType(payloadTypeName);
                if (resourceType == null)
                {
                    throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.BadRequest_InvalidTypeName(payloadTypeName));
                }

                if (!expectedType.IsAssignableFrom(resourceType))
                {
                    throw DataServiceException.CreateBadRequestError(Microsoft.OData.Service.Strings.BadRequest_InvalidTypeSpecified(payloadTypeName, expectedType.FullName));
                }
            }

            return(resourceType);
        }
Example #15
0
        public void AddTypeNameAnnotationAsNeeded_DoesNotAddAnnotation()
        {
            // Arrange
            ODataEnumValue        enumValue = new ODataEnumValue("value");
            IEdmEnumTypeReference enumType  = new EdmEnumTypeReference(
                new EdmEnumType("TestModel", "EnumType"), isNullable: false);

            // Act
            ODataEnumSerializer.AddTypeNameAnnotationAsNeeded(enumValue, enumType, ODataMetadataLevel.MinimalMetadata);

            // Assert
            SerializationTypeNameAnnotation annotation = enumValue.GetAnnotation <SerializationTypeNameAnnotation>();

            Assert.Null(annotation);
        }
Example #16
0
        private object ReadNonEntityValueImplementation(IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, bool epmPresent)
        {
            string itemTypeNameFromCollection;
            bool   flag;
            SerializationTypeNameAnnotation annotation;
            EdmTypeKind kind2;

            this.ReadNonEntityValueAttributes(out itemTypeNameFromCollection, out flag);
            if (flag)
            {
                return(this.ReadNullValue(expectedTypeReference, validateNullValue));
            }
            bool flag2 = false;

            if ((collectionValidator != null) && (itemTypeNameFromCollection == null))
            {
                itemTypeNameFromCollection = collectionValidator.ItemTypeNameFromCollection;
                flag2 = collectionValidator.ItemTypeKindFromCollection != EdmTypeKind.None;
            }
            IEdmTypeReference type = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(EdmTypeKind.None, edmStringType, expectedTypeReference, itemTypeNameFromCollection, base.Model, base.MessageReaderSettings, base.Version, new Func <EdmTypeKind>(this.GetNonEntityValueKind), out kind2, out annotation);

            if (flag2)
            {
                annotation = new SerializationTypeNameAnnotation {
                    TypeName = null
                };
            }
            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(itemTypeNameFromCollection, kind2);
            }
            switch (kind2)
            {
            case EdmTypeKind.Primitive:
                return(this.ReadPrimitiveValue(type.AsPrimitive()));

            case EdmTypeKind.Complex:
                return(this.ReadComplexValue((type == null) ? null : type.AsComplex(), itemTypeNameFromCollection, annotation, duplicatePropertyNamesChecker, epmPresent));

            case EdmTypeKind.Collection:
            {
                IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(type);
                return(this.ReadCollectionValue(collectionTypeReference, itemTypeNameFromCollection, annotation));
            }
            }
            throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue));
        }
Example #17
0
        /// <summary>
        /// Read a complex value from the reader.
        /// </summary>
        /// <param name="complexTypeReference">The type reference of the value to read (or null if no type is available).</param>
        /// <param name="payloadTypeName">The name of the type specified in the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the complex value (possibly null).</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param>
        /// <param name="epmPresent">Whether any EPM mappings exist.</param>
        /// <returns>The value read from the payload.</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element   - the element to read the value for.
        ///                  XmlNodeType.Attribute - an attribute on the element to read the value for.
        /// Post-Condition:  XmlNodeType.EndElement - the element has been read.
        ///
        /// Note that this method will not read null values, those should be handled by the caller already.
        /// </remarks>
        private ODataComplexValue ReadComplexValue(
            IEdmComplexTypeReference complexTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            bool epmPresent)
        {
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);

            this.IncreaseRecursionDepth();

            ODataComplexValue complexValue = new ODataComplexValue();
            IEdmComplexType   complexType  = complexTypeReference == null ? null : (IEdmComplexType)complexTypeReference.Definition;

            // If we have a metadata type for the complex value, use that type name
            // otherwise use the type name from the payload (if there was any).
            complexValue.TypeName = complexType == null ? payloadTypeName : complexType.ODataFullName();
            if (serializationTypeNameAnnotation != null)
            {
                complexValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            // Move to the element (so that if we were on an attribute we can test the element for being empty)
            this.XmlReader.MoveToElement();

            if (duplicatePropertyNamesChecker == null)
            {
                duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
            }
            else
            {
                duplicatePropertyNamesChecker.Clear();
            }

            List <ODataProperty> properties = new List <ODataProperty>();

            this.ReadPropertiesImplementation(complexType, properties, duplicatePropertyNamesChecker, epmPresent);
            complexValue.Properties = new ReadOnlyEnumerable <ODataProperty>(properties);

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(complexValue != null, "The method should never return null since it doesn't handle null values.");

            this.DecreaseRecursionDepth();

            return(complexValue);
        }
Example #18
0
        private ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, List <object> visitedComplexTypeObjects)
        {
            PrimitiveType                    type;
            string                           edmType;
            Func <object, object>            valueConverter = null;
            Func <object, ODataComplexValue> func2          = null;

            WebUtil.ValidateCollection(collectionItemType, value, propertyName);
            bool flag = PrimitiveType.TryGetPrimitiveType(collectionItemType, out type);
            ODataCollectionValue value2     = new ODataCollectionValue();
            IEnumerable          enumerable = (IEnumerable)value;

            if (flag)
            {
                edmType = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType);
                if (valueConverter == null)
                {
                    valueConverter = delegate(object val) {
                        WebUtil.ValidateCollectionItem(val);
                        WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType);
                        return(GetPrimitiveValue(val, collectionItemType));
                    };
                }
                value2.Items = Util.GetEnumerable <object>(enumerable, valueConverter);
            }
            else
            {
                edmType = this.requestInfo.ResolveNameFromType(collectionItemType);
                if (func2 == null)
                {
                    func2 = delegate(object val) {
                        WebUtil.ValidateCollectionItem(val);
                        WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType);
                        return(this.CreateODataComplexValue(collectionItemType, val, propertyName, true, visitedComplexTypeObjects));
                    };
                }
                value2.Items = Util.GetEnumerable <ODataComplexValue>(enumerable, func2);
            }
            string str2 = (edmType == null) ? null : string.Format(CultureInfo.InvariantCulture, "Collection({0})", new object[] { edmType });
            SerializationTypeNameAnnotation annotation = new SerializationTypeNameAnnotation {
                TypeName = str2
            };

            value2.SetAnnotation <SerializationTypeNameAnnotation>(annotation);
            return(value2);
        }
Example #19
0
        public void TypeNameShouldComeFromSerializationTypeNameAnnotationForCollectionValue()
        {
            var stna = new SerializationTypeNameAnnotation()
            {
                TypeName = "FromSTNA"
            };
            var value = new ODataCollectionValue()
            {
                TypeName = "Collection(Edm.String)"
            };

            value.SetAnnotation(stna);
            this.typeNameOracle.GetValueTypeNameForWriting(value,
                                                           EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(true)),
                                                           EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(false)),
                                                           /* isOpenProperty*/ false).Should().Be("FromSTNA");
        }
Example #20
0
        public void TypeNameShouldComeFromSerializationTypeNameAnnotationForComplexValue()
        {
            var stna = new SerializationTypeNameAnnotation()
            {
                TypeName = "FromSTNA"
            };
            var value = new ODataComplexValue()
            {
                TypeName = "Model.Bla"
            };

            value.SetAnnotation(stna);
            this.typeNameOracle.GetValueTypeNameForWriting(value,
                                                           new EdmComplexTypeReference(new EdmComplexType("Model", "Bla"), true),
                                                           new EdmComplexTypeReference(new EdmComplexType("Model", "Bla"), false),
                                                           /* isOpenProperty*/ false).Should().Be("FromSTNA");
        }
        /// <summary>
        /// Determines the type name to write to the payload. Json Light type names are only written into the payload for open properties
        /// or if the payload type name is more derived than the model type name.
        /// </summary>
        /// <param name="value">The ODataValue whose type name is to be written.</param>
        /// <param name="typeReferenceFromMetadata">The type as expected by the model.</param>
        /// <param name="typeReferenceFromValue">The type resolved from the value.</param>
        /// <param name="isOpenProperty">true if the type name belongs to an open property, false otherwise.</param>
        /// <returns>Type name to write to the payload, or null if no type should be written.</returns>
        internal override string GetValueTypeNameForWriting(
            ODataValue value,
            IEdmTypeReference typeReferenceFromMetadata,
            IEdmTypeReference typeReferenceFromValue,
            bool isOpenProperty)
        {
            SerializationTypeNameAnnotation typeNameAnnotation = value.GetAnnotation <SerializationTypeNameAnnotation>();

            if (typeNameAnnotation != null)
            {
                return(typeNameAnnotation.TypeName);
            }

            if (typeReferenceFromValue != null)
            {
                // Write type name when the type in the payload is more derived than the type from metadata.
                if (typeReferenceFromMetadata != null && typeReferenceFromMetadata.Definition.AsActualType().FullTypeName() != typeReferenceFromValue.FullName())
                {
                    return(typeReferenceFromValue.FullName());
                }

                // Note: When writing derived complexType value in a payload, we don't have the expected type.
                // So always write @odata.type for top-level derived complex type.
                if (typeReferenceFromMetadata == null && typeReferenceFromValue.IsComplex())
                {
                    if ((typeReferenceFromValue as IEdmComplexTypeReference).ComplexDefinition().BaseType != null)
                    {
                        return(typeReferenceFromValue.FullName());
                    }
                }

                // Do not write type name when the type is native json type.
                if (typeReferenceFromValue.IsPrimitive() && JsonSharedUtils.ValueTypeMatchesJsonType((ODataPrimitiveValue)value, typeReferenceFromValue.AsPrimitive()))
                {
                    return(null);
                }
            }

            if (!isOpenProperty)
            {
                // Do not write type name for non-open properties since we expect the reader to have an expected type (via API or context URI) and thus not need it.
                return(null);
            }

            return(GetTypeNameFromValue(value));
        }
Example #22
0
 protected override void StartEntry(ODataEntry entry)
 {
     this.CheckAndWriteParentNavigationLinkStartForInlineElement();
     if (entry != null)
     {
         this.StartEntryXmlCustomization(entry);
         this.atomOutputContext.XmlWriter.WriteStartElement("", "entry", "http://www.w3.org/2005/Atom");
         if (base.IsTopLevel)
         {
             this.atomEntryAndFeedSerializer.WriteBaseUriAndDefaultNamespaceAttributes();
         }
         string eTag = entry.ETag;
         if (eTag != null)
         {
             ODataAtomWriterUtils.WriteETag(this.atomOutputContext.XmlWriter, eTag);
         }
         AtomEntryScope    currentEntryScope = this.CurrentEntryScope;
         AtomEntryMetadata entryMetadata     = entry.Atom();
         string            id = entry.Id;
         if (id != null)
         {
             this.atomEntryAndFeedSerializer.WriteEntryId(id);
             currentEntryScope.SetWrittenElement(AtomElement.Id);
         }
         string typeName = entry.TypeName;
         SerializationTypeNameAnnotation annotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();
         if (annotation != null)
         {
             typeName = annotation.TypeName;
         }
         this.atomEntryAndFeedSerializer.WriteEntryTypeName(typeName, entryMetadata);
         Uri editLink = entry.EditLink;
         if (editLink != null)
         {
             this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata);
             currentEntryScope.SetWrittenElement(AtomElement.EditLink);
         }
         Uri readLink = entry.ReadLink;
         if (readLink != null)
         {
             this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata);
             currentEntryScope.SetWrittenElement(AtomElement.ReadLink);
         }
     }
 }
        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);
        }
        /// <summary>
        /// Determines the entity type name to write to the payload.
        /// </summary>
        /// <param name="expectedTypeName">The expected type name, e.g. the base type of the set or the nav prop.</param>
        /// <param name="entry">The ODataEntry whose type is to be written.</param>
        /// <returns>Type name to write to the payload, or null if no type name should be written.</returns>
        internal override string GetEntryTypeNameForWriting(string expectedTypeName, ODataEntry entry)
        {
            Debug.Assert(entry != null, "entry != null");

            SerializationTypeNameAnnotation typeNameAnnotation = entry.GetAnnotation <SerializationTypeNameAnnotation>();

            if (typeNameAnnotation != null)
            {
                return(typeNameAnnotation.TypeName);
            }

            // We only write entity type names in Json Light if it's more derived (different) from the expected type name.
            string entryTypeName = entry.TypeName;

            if (expectedTypeName != entryTypeName)
            {
                return(entryTypeName);
            }

            return(null);
        }
        /// <summary>
        /// Writes an enumeration value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the enumeration value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this enumeration value. The annotation value will override whatever type name is being written.</param>
        internal void WriteEnumValue(
            ODataEnumValue value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            Debug.Assert(value != null, "value != null");

            // write type name without validation:
            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, expectedTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);

            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");
            if (typeName != null)
            {
                Debug.Assert(typeName != EdmConstants.EdmStringTypeName, "Enum typeName != EdmConstants.StringTypeName");
                this.WritePropertyTypeAttribute(typeName);
            }

            // write string value without validation:
            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value.Value);
        }
Example #26
0
        internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable <LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage)
        {
            ClientEdmModel       model = ClientEdmModel.GetModel(this.requestInfo.MaxProtocolVersion);
            ClientTypeAnnotation clientTypeAnnotation = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));

            using (ODataMessageWriter writer = CreateMessageWriter(requestMessage, this.requestInfo))
            {
                ODataWriter odataWriter = writer.CreateODataEntryWriter();
                ODataEntry  entry       = new ODataEntry();
                if (this.requestInfo.HasWritingEventHandlers)
                {
                    entry.SetAnnotation <WritingEntityInfo>(new WritingEntityInfo(entityDescriptor.Entity, this.requestInfo));
                }
                string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor);
                if (clientTypeAnnotation.ElementTypeName != serverTypeName)
                {
                    SerializationTypeNameAnnotation annotation = new SerializationTypeNameAnnotation {
                        TypeName = serverTypeName
                    };
                    entry.SetAnnotation <SerializationTypeNameAnnotation>(annotation);
                }
                entry.TypeName = clientTypeAnnotation.ElementTypeName;
                if (EntityStates.Modified == entityDescriptor.State)
                {
                    entry.Id = entityDescriptor.GetLatestIdentity();
                }
                if (entityDescriptor.IsMediaLinkEntry || clientTypeAnnotation.IsMediaLinkEntry)
                {
                    entry.MediaResource = new ODataStreamReferenceValue();
                }
                odataWriter.WriteStart(entry);
                if (EntityStates.Added == entityDescriptor.State)
                {
                    this.WriteNavigationLink(entityDescriptor, relatedLinks, odataWriter);
                }
                entry.Properties = this.PopulateProperties(clientTypeAnnotation, entityDescriptor.Entity, null);
                odataWriter.WriteEnd();
            }
        }
Example #27
0
        /// <summary>
        /// Writes a primitive value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the primitive value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this primitive value. The annotation value will override whatever type name is being written.</param>
        internal void WritePrimitiveValue(
            object value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(value != null, "value != null");

            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType());

            if (primitiveTypeReference == null)
            {
                throw new ODataException(ODataErrorStrings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName));
            }

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive);
            }

            if (expectedTypeReference != null)
            {
                ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference);
            }

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, primitiveTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);

            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            if (typeName != null && typeName != EdmConstants.EdmStringTypeName)
            {
                this.WritePropertyTypeAttribute(typeName);
            }

            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value);
        }
        /// <summary>
        /// Determines the type name to write to the payload.  Json Light type names are only written into the payload for open properties
        /// or if the payload type name is more derived than the model type name.
        /// </summary>
        /// <param name="value">The ODataValue whose type name is to be written.</param>
        /// <param name="typeReferenceFromMetadata">The type as expected by the model.</param>
        /// <param name="typeReferenceFromValue">The type resolved from the value.</param>
        /// <param name="isOpenProperty">true if the type name belongs to an open property, false otherwise.</param>
        /// <returns>Type name to write to the payload, or null if no type should be written.</returns>
        internal override string GetValueTypeNameForWriting(
            ODataValue value,
            IEdmTypeReference typeReferenceFromMetadata,
            IEdmTypeReference typeReferenceFromValue,
            bool isOpenProperty)
        {
            DebugUtils.CheckNoExternalCallers();

            SerializationTypeNameAnnotation typeNameAnnotation = value.GetAnnotation <SerializationTypeNameAnnotation>();

            if (typeNameAnnotation != null)
            {
                return(typeNameAnnotation.TypeName);
            }

            if (typeReferenceFromValue != null)
            {
                // Write type name when the type in the payload is more derived than the type from metadata.
                if (typeReferenceFromMetadata != null && typeReferenceFromMetadata.ODataFullName() != typeReferenceFromValue.ODataFullName())
                {
                    return(typeReferenceFromValue.ODataFullName());
                }

                // Do not write type name when the type is native json type.
                if (typeReferenceFromValue.IsPrimitive() && JsonSharedUtils.ValueTypeMatchesJsonType((ODataPrimitiveValue)value, typeReferenceFromValue.AsPrimitive()))
                {
                    return(null);
                }
            }

            if (!isOpenProperty)
            {
                // Do not write type name for non-open properties since we expect the reader to have an expected type (via API or metadata URI) and thus not need it.
                return(null);
            }

            return(GetTypeNameFromValue(value));
        }
Example #29
0
 public void TypeNameShouldComeFromSerializationTypeNameAnnotationForPrimitiveValue()
 {
     var stna = new SerializationTypeNameAnnotation() {TypeName = "FromSTNA"};
     var value = new ODataPrimitiveValue(42);
     value.SetAnnotation(stna);
     this.typeNameOracle.GetValueTypeNameForWriting(value,
         EdmCoreModel.Instance.GetInt32(true),
         EdmCoreModel.Instance.GetInt32(false),
         /* isOpenProperty*/ false).Should().Be("FromSTNA");
 }
Example #30
0
        /// <summary>
        /// Read a collection from the reader.
        /// </summary>
        /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param>
        /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param>
        /// <returns>The value read from the payload.</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element   - the element to read the value for.
        ///                  XmlNodeType.Attribute - an attribute on the element to read the value for.
        /// Post-Condition:  XmlNodeType.Element    - the element was empty.
        ///                  XmlNodeType.EndElement - the element had some value.
        ///
        /// Note that this method will not read null values, those should be handled by the caller already.
        /// </remarks>
        private ODataCollectionValue ReadCollectionValue(
            IEdmCollectionTypeReference collectionTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);
            Debug.Assert(
                collectionTypeReference == null || collectionTypeReference.IsNonEntityODataCollectionTypeKind(),
                "If the metadata is specified it must denote a collection for this method to work.");

            this.IncreaseRecursionDepth();

            ODataCollectionValue collectionValue = new ODataCollectionValue();

            // If we have a metadata type for the collection, use that type name
            // otherwise use the type name from the payload (if there was any).
            collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName();
            if (serializationTypeNameAnnotation != null)
            {
                collectionValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            // Move to the element (so that if we were on an attribute we can test the element for being empty)
            this.XmlReader.MoveToElement();

            List <object> items = new List <object>();

            // Empty collections are valid - they have no items
            if (!this.XmlReader.IsEmptyElement)
            {
                // Read over the collection element to its first child node (or end-element)
                this.XmlReader.ReadStartElement();

                IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                DuplicatePropertyNamesChecker          duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                CollectionWithoutExpectedTypeValidator collectionValidator           = null;
                if (collectionTypeReference == null)
                {
                    // Parse the type name from the payload (if any), extract the item type name and construct a collection validator
                    string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName);
                    collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName);
                }

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                    case XmlNodeType.Element:
                        if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                        {
                            if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
                            {
                                throw new ODataException(o.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataNamespace));
                            }

                            // We don't support EPM for collections so it is fine to say that EPM is not present
                            object itemValue = this.ReadNonEntityValueImplementation(
                                itemTypeReference,
                                duplicatePropertyNamesChecker,
                                collectionValidator,
                                /*validateNullValue*/ true,
                                /*epmPresent*/ false);

                            // read over the end tag of the element or the start tag if the element was empty.
                            this.XmlReader.Read();

                            // Validate the item (for example that it's not null)
                            ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */);

                            // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                            // the expected type (the itemType).
                            items.Add(itemValue);
                        }
                        else
                        {
                            this.XmlReader.Skip();
                        }

                        break;

                    case XmlNodeType.EndElement:
                        // End of the collection.
                        break;

                    default:
                        // Non-element so for example a text node, just ignore
                        this.XmlReader.Skip();
                        break;
                    }
                }while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            }

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values.");

            this.DecreaseRecursionDepth();

            return(collectionValue);
        }
Example #31
0
 public void TypeNameShouldComeFromSerializationTypeNameAnnotationForCollectionValue()
 {
     var stna = new SerializationTypeNameAnnotation() {TypeName = "FromSTNA"};
     var value = new ODataCollectionValue() {TypeName = "Collection(Edm.String)"};
     value.SetAnnotation(stna);
     this.typeNameOracle.GetValueTypeNameForWriting(value,
         EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(true)),
         EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(false)),
         /* isOpenProperty*/ false).Should().Be("FromSTNA");
 }
Example #32
0
        /// <summary>
        /// Reads the primitive, complex or collection value.
        /// </summary>
        /// <param name="expectedTypeReference">The expected type reference of the value.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param>
        /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param>
        /// <param name="validateNullValue">true to validate a null value; otherwise false.</param>
        /// <param name="epmPresent">Whether any EPM mappings exist.</param>
        /// <returns>The value read (null, primitive CLR value, ODataComplexValue or ODataCollectionValue).</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element    - The XML element containing the value to read (also the attributes will be read from it)
        /// Post-Condition:  XmlNodeType.EndElement - The end tag of the element.
        ///                  XmlNodeType.Element    - The empty element node.
        /// </remarks>
        private object ReadNonEntityValueImplementation(
            IEdmTypeReference expectedTypeReference,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            bool validateNullValue,
            bool epmPresent)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                expectedTypeReference == null || !expectedTypeReference.IsODataEntityTypeKind(),
                "Only primitive, complex or collection types can be read by this method.");
            Debug.Assert(
                expectedTypeReference == null || collectionValidator == null,
                "If an expected value type reference is specified, no collection validator must be provided.");
            this.XmlReader.AssertNotBuffering();

            // Read the attributes looking for m:type and m:null
            string payloadTypeName;
            bool   isNull;

            this.ReadNonEntityValueAttributes(out payloadTypeName, out isNull);

            object result;

            if (isNull)
            {
                result = this.ReadNullValue(expectedTypeReference, validateNullValue);
            }
            else
            {
                // If we could derive the item type name from the collection's type name and no type name was specified in the payload
                // fill it in now.
                EdmTypeKind payloadTypeKind;
                bool        derivedItemTypeNameFromCollectionTypeName = false;
                if (collectionValidator != null && payloadTypeName == null)
                {
                    payloadTypeName = collectionValidator.ItemTypeNameFromCollection;
                    payloadTypeKind = collectionValidator.ItemTypeKindFromCollection;
                    derivedItemTypeNameFromCollectionTypeName = payloadTypeKind != EdmTypeKind.None;
                }

                // Resolve the payload type name and compute the target type kind and target type reference.
                SerializationTypeNameAnnotation serializationTypeNameAnnotation;
                EdmTypeKind       targetTypeKind;
                IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(
                    EdmTypeKind.None,
                    /*defaultPrimitivePayloadType*/ edmStringType,
                    expectedTypeReference,
                    payloadTypeName,
                    this.Model,
                    this.MessageReaderSettings,
                    this.Version,
                    this.GetNonEntityValueKind,
                    out targetTypeKind,
                    out serializationTypeNameAnnotation);

                if (derivedItemTypeNameFromCollectionTypeName)
                {
                    Debug.Assert(
                        serializationTypeNameAnnotation == null,
                        "If we derived the item type name from the collection type name we must not have created a serialization type name annotation.");
                    serializationTypeNameAnnotation = new SerializationTypeNameAnnotation {
                        TypeName = null
                    };
                }

                // If we have no expected type make sure the collection items are of the same kind and specify the same name.
                if (collectionValidator != null)
                {
                    Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference.");
                    collectionValidator.ValidateCollectionItem(payloadTypeName, targetTypeKind);
                }

                switch (targetTypeKind)
                {
                case EdmTypeKind.Primitive:
                    Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type.");
                    result = this.ReadPrimitiveValue(targetTypeReference.AsPrimitive());
                    break;

                case EdmTypeKind.Complex:
                    Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type.");
                    result = this.ReadComplexValue(
                        targetTypeReference == null ? null : targetTypeReference.AsComplex(),
                        payloadTypeName,
                        serializationTypeNameAnnotation,
                        duplicatePropertyNamesChecker,
                        epmPresent);
                    break;

                case EdmTypeKind.Collection:
                    IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference);
                    result = this.ReadCollectionValue(
                        collectionTypeReference,
                        payloadTypeName,
                        serializationTypeNameAnnotation);
                    break;

                default:
                    throw new ODataException(o.Strings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue));
                }
            }

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            this.XmlReader.AssertNotBuffering();
            return(result);
        }
        /// <summary>
        /// Writes an enumeration value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the enumeration value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this enumeration value. The annotation value will override whatever type name is being written.</param>
        internal void WriteEnumValue(
            ODataEnumValue value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            Debug.Assert(value != null, "value != null");

            // write type name without validation:
            // TODO: consider adding a overloaded GetValueTypeNameForWriting() without out parameter.
            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, expectedTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);
            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");
            if (typeName != null)
            {
                Debug.Assert(typeName != EdmConstants.EdmStringTypeName, "Enum typeName != EdmConstants.StringTypeName");
                this.WritePropertyTypeAttribute(typeName);
            }

            // write string value without validation:
            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value.Value);
        }
        /// <summary>
        /// Writes a primitive value.
        /// </summary>
        /// <param name="value">The value to write.</param>
        /// <param name="collectionValidator">The collection validator instance.</param>
        /// <param name="expectedTypeReference">The expected type of the primitive value.</param>
        /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this primitive value. The annotation value will override whatever type name is being written.</param>
        internal void WritePrimitiveValue(
            object value,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            IEdmTypeReference expectedTypeReference,
            SerializationTypeNameAnnotation typeNameAnnotation)
        {
            Debug.Assert(value != null, "value != null");

            IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType());
            if (primitiveTypeReference == null)
            {
                throw new ODataException(ODataErrorStrings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName));
            }

            if (collectionValidator != null)
            {
                collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive);
            }

            if (expectedTypeReference != null)
            {
                ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference);
            }

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, primitiveTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName);
            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            if (typeName != null && typeName != EdmConstants.EdmStringTypeName)
            {
                this.WritePropertyTypeAttribute(typeName);
            }

            AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value);
        }
        /// <summary>
        /// Reads a complex value.
        /// </summary>
        /// <param name="insideJsonObjectValue">true if the reader is positioned on the first property of the value which is a JSON Object 
        ///     (or the second property if the first one was odata.type).</param>
        /// <param name="insideComplexValue">true if we are reading a complex value and the reader is already positioned inside the complex value; otherwise false.</param>
        /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param>
        /// <param name="complexValueTypeReference">The expected type reference of the value.</param>
        /// <param name="payloadTypeName">The type name read from the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use - this is always initialized as necessary, do not clear.</param>
        /// <returns>The value of the complex value.</returns>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.Property - the first property of the complex value object, or the second one if the first one was odata.type.
        ///                 JsonNodeType.EndObject - the end object of the complex value object.
        /// Post-Condition: almost anything - the node after the complex value (after the EndObject)
        /// </remarks>
        private ODataComplexValue ReadComplexValue(
            bool insideJsonObjectValue,
            bool insideComplexValue,
            string propertyName,
            IEdmComplexTypeReference complexValueTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            if (!insideJsonObjectValue && !insideComplexValue)
            {
                if (this.JsonReader.NodeType != JsonNodeType.StartObject)
                {
                    string typeName = complexValueTypeReference != null ? complexValueTypeReference.FullName() : payloadTypeName;
                    throw new ODataException(
                        string.Format(CultureInfo.InvariantCulture,
                        "The property with name '{0}' was found with a value node of type '{1}'; however, a complex value of type '{2}' was expected.",
                        propertyName, this.JsonReader.NodeType, typeName));
                }

                this.JsonReader.Read();
            }

            return this.ReadComplexValue(complexValueTypeReference, payloadTypeName, serializationTypeNameAnnotation, duplicatePropertyNamesChecker);
        }
Example #36
0
 public void TypeNameShouldComeFromSerializationTypeNameAnnotationForComplexValue()
 {
     var stna = new SerializationTypeNameAnnotation() {TypeName = "FromSTNA"};
     var value = new ODataComplexValue() {TypeName = "Model.Bla"};
     value.SetAnnotation(stna);
     this.typeNameOracle.GetValueTypeNameForWriting(value,
         new EdmComplexTypeReference(new EdmComplexType("Model", "Bla"), true),
         new EdmComplexTypeReference(new EdmComplexType("Model", "Bla"), false),
         /* isOpenProperty*/ false).Should().Be("FromSTNA");
 }
        /// <summary>
        /// Read a collection from the reader.
        /// </summary>
        /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param>
        /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param>
        /// <returns>The value read from the payload.</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element   - the element to read the value for.
        ///                  XmlNodeType.Attribute - an attribute on the element to read the value for.
        /// Post-Condition:  XmlNodeType.Element    - the element was empty.
        ///                  XmlNodeType.EndElement - the element had some value.
        ///                  
        /// Note that this method will not read null values, those should be handled by the caller already.
        /// </remarks>
        private ODataCollectionValue ReadCollectionValue(
            IEdmCollectionTypeReference collectionTypeReference, 
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);
            Debug.Assert(
                collectionTypeReference == null || collectionTypeReference.IsNonEntityCollectionType(),
                "If the metadata is specified it must denote a collection for this method to work.");

            this.IncreaseRecursionDepth();

            ODataCollectionValue collectionValue = new ODataCollectionValue();

            // If we have a metadata type for the collection, use that type name
            // otherwise use the type name from the payload (if there was any).
            collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName();
            if (serializationTypeNameAnnotation != null)
            {
                collectionValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            // Move to the element (so that if we were on an attribute we can test the element for being empty)
            this.XmlReader.MoveToElement();

            List<object> items = new List<object>();

            // Empty collections are valid - they have no items
            if (!this.XmlReader.IsEmptyElement)
            {
                // Read over the collection element to its first child node (or end-element)
                this.XmlReader.ReadStartElement();

                // If we don't have metadata (that is collectionType is null) we parse the type name
                // and extract the item type name from it. Then we create a CollectionWithoutExpectedTypeValidator
                // instance and use it to ensure that all item types read for the collection value are consistent with
                // the item type derived from the collection value's type name.
                // Note that if an item does not specify a type name but the collection value does, the item will be
                // assigned the item type name computed from the collection value's type.
                // Note that JSON doesn't have this problem since in JSON we always have metadata and thus the collectionType
                // is never null by the time we get to a similar place in the code.
                IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                CollectionWithoutExpectedTypeValidator collectionValidator = null;
                if (collectionTypeReference == null)
                {
                    // Parse the type name from the payload (if any), extract the item type name and construct a collection validator
                    string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName);
                    collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName);
                }

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                        case XmlNodeType.Element:
                            if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace))
                            {
                                if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
                                {
                                    this.XmlReader.Skip();
                                }
                                else
                                {
                                    object itemValue = this.ReadNonEntityValueImplementation(
                                        itemTypeReference,
                                        duplicatePropertyNamesChecker,
                                        collectionValidator,
                                        /*validateNullValue*/ true,
                                        /*propertyName*/ null);

                                    // read over the end tag of the element or the start tag if the element was empty.
                                    this.XmlReader.Read();

                                    // Validate the item (for example that it's not null)
                                    ValidationUtils.ValidateCollectionItem(itemValue, itemTypeReference.IsNullable());

                                    // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                                    // the expected type (the itemType).
                                    items.Add(itemValue);
                                }
                            }
                            else if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                            {
                                throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataMetadataNamespace));  
                            }
                            else
                            {
                                this.XmlReader.Skip();
                            }

                            break;

                        case XmlNodeType.EndElement:
                            // End of the collection.
                            break;

                        default:
                            // Non-element so for example a text node, just ignore
                            this.XmlReader.Skip();
                            break;
                    }
                }
                while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            }

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values.");

            this.DecreaseRecursionDepth();

            return collectionValue;
        }
        /// <summary>
        /// Read a complex value from the reader.
        /// </summary>
        /// <param name="complexTypeReference">The type reference of the value to read (or null if no type is available).</param>
        /// <param name="payloadTypeName">The name of the type specified in the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the complex value (possibly null).</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param>
        /// <returns>The value read from the payload.</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element   - the element to read the value for.
        ///                  XmlNodeType.Attribute - an attribute on the element to read the value for.
        /// Post-Condition:  XmlNodeType.EndElement - the element has been read.
        ///                  
        /// Note that this method will not read null values, those should be handled by the caller already.
        /// </remarks>
        private ODataComplexValue ReadComplexValue(
            IEdmComplexTypeReference complexTypeReference, 
            string payloadTypeName, 
            SerializationTypeNameAnnotation serializationTypeNameAnnotation,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);

            this.IncreaseRecursionDepth();

            ODataComplexValue complexValue = new ODataComplexValue();
            IEdmComplexType complexType = complexTypeReference == null ? null : (IEdmComplexType)complexTypeReference.Definition;

            // If we have a metadata type for the complex value, use that type name
            // otherwise use the type name from the payload (if there was any).
            complexValue.TypeName = complexType == null ? payloadTypeName : complexType.ODataFullName();
            if (serializationTypeNameAnnotation != null)
            {
                complexValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            // Move to the element (so that if we were on an attribute we can test the element for being empty)
            this.XmlReader.MoveToElement();

            if (duplicatePropertyNamesChecker == null)
            {
                duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
            }
            else
            {
                duplicatePropertyNamesChecker.Clear();
            }

            ReadOnlyEnumerable<ODataProperty> properties = new ReadOnlyEnumerable<ODataProperty>();
            this.ReadPropertiesImplementation(complexType, properties, duplicatePropertyNamesChecker);
            complexValue.Properties = properties;

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(complexValue != null, "The method should never return null since it doesn't handle null values.");

            this.DecreaseRecursionDepth();

            return complexValue;
        }
        /// <summary>
        /// Reads the primitive, complex or collection value.
        /// </summary>
        /// <param name="expectedTypeReference">The expected type reference of the value.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param>
        /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param>
        /// <param name="validateNullValue">true to validate a null value (i.e., throw if a null value is being written for a non-nullable property); otherwise false.</param>
        /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param>
        /// <returns>The value read (null, primitive CLR value, ODataComplexValue or ODataCollectionValue).</returns>
        /// <remarks>
        /// Pre-Condition:   XmlNodeType.Element    - The XML element containing the value to read (also the attributes will be read from it)
        /// Post-Condition:  XmlNodeType.EndElement - The end tag of the element.
        ///                  XmlNodeType.Element    - The empty element node.
        /// </remarks>
        private object ReadNonEntityValueImplementation(IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, string propertyName)
        {
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(
                expectedTypeReference == null || !expectedTypeReference.IsODataEntityTypeKind(),
                "Only primitive, complex or collection types can be read by this method.");
            Debug.Assert(
                expectedTypeReference == null || collectionValidator == null,
                "If an expected value type reference is specified, no collection validator must be provided.");
            this.XmlReader.AssertNotBuffering();

            // Read the attributes looking for m:type and m:null
            string payloadTypeName;
            bool isNull;
            this.ReadNonEntityValueAttributes(out payloadTypeName, out isNull);

            object result;
            if (isNull)
            {
                result = this.ReadNullValue(expectedTypeReference, validateNullValue, propertyName);
            }
            else
            {
                // If we could derive the item type name from the collection's type name and no type name was specified in the payload
                // fill it in now.
                EdmTypeKind payloadTypeKind;
                bool derivedItemTypeNameFromCollectionTypeName = false;
                if (collectionValidator != null && payloadTypeName == null)
                {
                    payloadTypeName = collectionValidator.ItemTypeNameFromCollection;
                    payloadTypeKind = collectionValidator.ItemTypeKindFromCollection;
                    derivedItemTypeNameFromCollectionTypeName = payloadTypeKind != EdmTypeKind.None;
                }

                // Resolve the payload type name and compute the target type kind and target type reference.
                SerializationTypeNameAnnotation serializationTypeNameAnnotation;
                EdmTypeKind targetTypeKind;
                IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(
                    EdmTypeKind.None,
                    /*defaultPrimitivePayloadType*/ edmStringType,
                    expectedTypeReference,
                    payloadTypeName,
                    this.Model,
                    this.MessageReaderSettings,
                    this.GetNonEntityValueKind,
                    out targetTypeKind,
                    out serializationTypeNameAnnotation);

                if (derivedItemTypeNameFromCollectionTypeName)
                {
                    Debug.Assert(
                        serializationTypeNameAnnotation == null,
                        "If we derived the item type name from the collection type name we must not have created a serialization type name annotation.");
                    serializationTypeNameAnnotation = new SerializationTypeNameAnnotation { TypeName = null };
                }

                // If we have no expected type make sure the collection items are of the same kind and specify the same name.
                if (collectionValidator != null)
                {
                    Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference.");
                    collectionValidator.ValidateCollectionItem(payloadTypeName, targetTypeKind);
                }

                switch (targetTypeKind)
                {
                    case EdmTypeKind.Enum:
                        Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataEnumTypeKind(), "Expected an OData Enum type.");
                        result = this.ReadEnumValue(targetTypeReference.AsEnum());
                        break;

                    case EdmTypeKind.Primitive:
                        Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type.");
                        result = this.ReadPrimitiveValue(targetTypeReference.AsPrimitive());
                        break;

                    case EdmTypeKind.Complex:
                        Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type.");
                        result = this.ReadComplexValue(
                            targetTypeReference == null ? null : targetTypeReference.AsComplex(),
                            payloadTypeName,
                            serializationTypeNameAnnotation,
                            duplicatePropertyNamesChecker);
                        break;

                    case EdmTypeKind.Collection:
                        IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference);
                        result = this.ReadCollectionValue(
                            collectionTypeReference, 
                            payloadTypeName,
                            serializationTypeNameAnnotation);
                        break;

                    default:
                        throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue));
                }
            }

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            this.XmlReader.AssertNotBuffering();
            return result;
        }
        /// <summary>
        /// Reads a collection value.
        /// </summary>
        /// <param name="collectionValueTypeReference">The collection type reference of the value.</param>
        /// <param name="payloadTypeName">The type name read from the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param>
        /// <returns>The value of the collection.</returns>
        /// <remarks>
        /// Pre-Condition:  Fails if the current node is not a JsonNodeType.StartArray
        /// Post-Condition: almost anything - the node after the collection value (after the EndArray)
        /// </remarks>
        private ODataCollectionValue ReadCollectionValue(
            IEdmCollectionTypeReference collectionValueTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            Debug.Assert(
                collectionValueTypeReference == null || collectionValueTypeReference.IsNonEntityCollectionType(),
                "If the metadata is specified it must denote a Collection for this method to work.");

            this.IncreaseRecursionDepth();

            // Read over the start array
            this.JsonReader.ReadStartArray();

            ODataCollectionValue collectionValue = new ODataCollectionValue();
            collectionValue.TypeName = collectionValueTypeReference != null ? collectionValueTypeReference.FullName() : payloadTypeName;
            if (serializationTypeNameAnnotation != null)
            {
                collectionValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            if (collectionValueTypeReference != null)
            {
                collectionValue.SetAnnotation(new ODataTypeAnnotation(collectionValueTypeReference));
            }

            List<object> items = new List<object>();
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
            IEdmTypeReference itemType = null;
            if (collectionValueTypeReference != null)
            {
                itemType = collectionValueTypeReference.CollectionDefinition().ElementType;
            }

            // NOTE: we do not support reading JSON Light without metadata right now so we always have an expected item type;
            //       The collection validator is always null.
            CollectionWithoutExpectedTypeValidator collectionValidator = null;

            while (this.JsonReader.NodeType != JsonNodeType.EndArray)
            {
                object itemValue = this.ReadNonEntityValueImplementation(
                    /*payloadTypeName*/ null,
                    itemType,
                    duplicatePropertyNamesChecker,
                    collectionValidator,
                    /*validateNullValue*/ true,
                    /*isTopLevelPropertyValue*/ false,
                    /*insideComplexValue*/ false,
                    /*propertyName*/ null);

                // Validate the item (for example that it's not null)
                ValidationUtils.ValidateCollectionItem(itemValue, itemType.IsNullable());

                // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                // the expected type (the itemType).
                items.Add(itemValue);
            }

            Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "The results value must end with an end array.");
            this.JsonReader.ReadEndArray();

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.DecreaseRecursionDepth();

            return collectionValue;
        }
        /// <summary>
        /// Reads a complex value.
        /// </summary>
        /// <param name="complexValueTypeReference">The expected type reference of the value.</param>
        /// <param name="payloadTypeName">The type name read from the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use - this is always initialized as necessary, do not clear.</param>
        /// <returns>The value of the complex value.</returns>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.Property - the first property of the complex value object, or the second one if the first one was odata.type.
        ///                 JsonNodeType.EndObject - the end object of the complex value object.
        /// Post-Condition: almost anything - the node after the complex value (after the EndObject)
        /// </remarks>
        private ODataComplexValue ReadComplexValue(
            IEdmComplexTypeReference complexValueTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null");

            this.IncreaseRecursionDepth();

            ODataComplexValue complexValue = new ODataComplexValue();
            complexValue.TypeName = complexValueTypeReference != null ? complexValueTypeReference.FullName() : payloadTypeName;
            if (serializationTypeNameAnnotation != null)
            {
                complexValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            if (complexValueTypeReference != null)
            {
                complexValue.SetAnnotation(new ODataTypeAnnotation(complexValueTypeReference));
            }

            List<ODataProperty> properties = new List<ODataProperty>();
            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                this.ReadPropertyCustomAnnotationValue = this.ReadCustomInstanceAnnotationValue;
                this.ProcessProperty(
                    duplicatePropertyNamesChecker,
                    this.ReadTypePropertyAnnotationValue,
                    (propertyParsingResult, propertyName) =>
                    {
                        switch (propertyParsingResult)
                        {
                            case PropertyParsingResult.ODataInstanceAnnotation:
                                if (string.CompareOrdinal(ODataAnnotationNames.ODataType, propertyName) == 0)
                                {
                                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_ComplexTypeAnnotationNotFirst);
                                }
                                else
                                {
                                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName));
                                }

                            case PropertyParsingResult.CustomInstanceAnnotation:
                                ODataAnnotationNames.ValidateIsCustomAnnotationName(propertyName);
                                Debug.Assert(
                                    !this.MessageReaderSettings.ShouldSkipAnnotation(propertyName),
                                    "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(annotationName) -- otherwise we should have already skipped the custom annotation and won't see it here.");
                                var customInstanceAnnotationValue = this.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, propertyName);
                                complexValue.InstanceAnnotations.Add(new ODataInstanceAnnotation(propertyName, customInstanceAnnotationValue.ToODataValue()));
                                break;

                            case PropertyParsingResult.PropertyWithoutValue:
                                throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_ComplexValuePropertyAnnotationWithoutProperty(propertyName));

                            case PropertyParsingResult.PropertyWithValue:
                                // Any other property is data
                                ODataProperty property = new ODataProperty();
                                property.Name = propertyName;

                                // Lookup the property in metadata
                                IEdmProperty edmProperty = null;
                                bool ignoreProperty = false;
                                if (complexValueTypeReference != null)
                                {
                                    edmProperty = ReaderValidationUtils.ValidateValuePropertyDefined(propertyName, complexValueTypeReference.ComplexDefinition(), this.MessageReaderSettings, out ignoreProperty);
                                }

                                if (ignoreProperty
                                    && (this.JsonReader.NodeType == JsonNodeType.StartObject || this.JsonReader.NodeType == JsonNodeType.StartArray))
                                {
                                    this.JsonReader.SkipValue();
                                }
                                else
                                {
                                    // EdmLib bridge marks all key properties as non-nullable, but Astoria allows them to be nullable.
                                    // If the property has an annotation to ignore null values, we need to omit the property in requests.
                                    ODataNullValueBehaviorKind nullValueReadBehaviorKind = this.ReadingResponse || edmProperty == null
                                        ? ODataNullValueBehaviorKind.Default
                                        : this.Model.NullValueReadBehaviorKind(edmProperty);

                                    // Read the property value
                                    object propertyValue = this.ReadNonEntityValueImplementation(
                                        ValidateDataPropertyTypeNameAnnotation(duplicatePropertyNamesChecker, propertyName),
                                        edmProperty == null ? null : edmProperty.Type,
                                        /*duplicatePropertyNamesChecker*/ null,
                                        /*collectionValidator*/ null,
                                        nullValueReadBehaviorKind == ODataNullValueBehaviorKind.Default,
                                        /*isTopLevelPropertyValue*/ false,
                                        /*insideComplexValue*/ false,
                                        propertyName,
                                        edmProperty == null);

                                    if (nullValueReadBehaviorKind != ODataNullValueBehaviorKind.IgnoreValue || propertyValue != null)
                                    {
                                        duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
                                        property.Value = propertyValue;
                                        var propertyAnnotations = duplicatePropertyNamesChecker.GetCustomPropertyAnnotations(propertyName);
                                        if (propertyAnnotations != null)
                                        {
                                            foreach (var annotation in propertyAnnotations)
                                            {
                                                if (annotation.Value != null)
                                                {
                                                    // annotation.Value == null indicates that this annotation should be skipped.
                                                    property.InstanceAnnotations.Add(new ODataInstanceAnnotation(annotation.Key, annotation.Value.ToODataValue()));
                                                }
                                            }
                                        }

                                        properties.Add(property);
                                    }
                                }

                                break;

                            case PropertyParsingResult.EndOfObject:
                                break;

                            case PropertyParsingResult.MetadataReferenceProperty:
                                throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));
                        }
                    });
            }

            Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndObject, "After all the properties of a complex value are read the EndObject node is expected.");
            this.JsonReader.ReadEndObject();

            complexValue.Properties = new ReadOnlyEnumerable<ODataProperty>(properties);

            this.DecreaseRecursionDepth();

            return complexValue;
        }