Esempio n. 1
0
        private void ResolveEnumValueTypeName(ODataEnumValue enumValue, bool isOpenPropertyType)
        {
            if (this.currentPropertyInfo.ValueType == null || this.currentPropertyInfo.ValueType.TypeName != enumValue.TypeName)
            {
                IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeForEnumValue(
                    this.Model,
                    enumValue,
                    isOpenPropertyType);

                // This is a work around, needTypeOnWire always = true for client side:
                // ClientEdmModel's reflection can't know a property is open type even if it is, so here
                // make client side always write 'odata.type' for enum.
                bool needTypeOnWire = string.Equals(this.JsonLightOutputContext.Model.GetType().Name, "ClientEdmModel",
                                                    StringComparison.OrdinalIgnoreCase);
                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(
                    enumValue, this.currentPropertyInfo.MetadataType.TypeReference, typeFromValue, needTypeOnWire || isOpenPropertyType);

                this.currentPropertyInfo.ValueType       = new PropertyValueTypeInfo(enumValue.TypeName, typeFromValue);
                this.currentPropertyInfo.TypeNameToWrite = typeNameToWrite;
            }
            else
            {
                string typeNameToWrite;
                if (TypeNameOracle.TryGetTypeNameFromAnnotation(enumValue, out typeNameToWrite))
                {
                    this.currentPropertyInfo.TypeNameToWrite = typeNameToWrite;
                }
            }
        }
Esempio n. 2
0
        /// <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="propertyInfo">The serialization info of current property</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,
            PropertySerializationInfo propertyInfo,
            bool isOpenProperty)
        {
            PropertyValueTypeInfo valueType = propertyInfo.ValueType;

            string typeNameToWrite;

            if (TypeNameOracle.TryGetTypeNameFromAnnotation(value, out typeNameToWrite))
            {
                return(typeNameToWrite);
            }

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

            return(GetTypeNameFromValue(value));
        }
Esempio n. 3
0
        private void ResolveCollectionValueTypeName(ODataCollectionValue collectionValue, bool isOpenPropertyType)
        {
            if (this.currentPropertyInfo.ValueType == null || this.currentPropertyInfo.ValueType.TypeName != collectionValue.TypeName)
            {
                IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeForCollectionValue(
                    this.Model,
                    this.currentPropertyInfo.MetadataType.TypeReference,
                    collectionValue,
                    isOpenPropertyType,
                    this.WriterValidator);

                this.currentPropertyInfo.ValueType       = new PropertyValueTypeInfo(collectionValue.TypeName, typeFromValue);
                this.currentPropertyInfo.TypeNameToWrite =
                    this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue,
                                                                                          this.currentPropertyInfo, isOpenPropertyType);
            }
            else
            {
                string typeNameToWrite;
                if (TypeNameOracle.TryGetTypeNameFromAnnotation(collectionValue, out typeNameToWrite))
                {
                    this.currentPropertyInfo.TypeNameToWrite = typeNameToWrite;
                }
            }
        }
        /// <summary>
        /// Asynchronously writes out the value of a resource (complex or entity).
        /// </summary>
        /// <param name="resourceValue">The resource (complex or entity) value to write.</param>
        /// <param name="metadataTypeReference">The metadata type for the resource value.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open (dynamic) property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <remarks>The current recursion depth should be a value, measured by the number of resource and collection values between
        /// this resource value and the top-level payload, not including this one.</remarks>
        /// <returns>A task that represents the asynchronous write operation.</returns>
        public virtual async Task WriteResourceValueAsync(
            ODataResourceValue resourceValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType,
            IDuplicatePropertyNameChecker duplicatePropertyNamesChecker)
        {
            Debug.Assert(resourceValue != null, "resourceValue != null");

            this.IncreaseRecursionDepth();

            // Start the object scope which will represent the entire resource instance;
            await this.AsynchronousJsonWriter.StartObjectScopeAsync().ConfigureAwait(false);

            string typeName = resourceValue.TypeName;

            // In requests, we allow the property type reference to be null if the type name is specified in the OM
            if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel())
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForResourceValueRequest);
            }

            // Resolve the type name to the type; if no type name is specified we will use the type inferred from metadata.
            IEdmStructuredTypeReference resourceValueTypeReference =
                (IEdmStructuredTypeReference)TypeNameOracle.ResolveAndValidateTypeForResourceValue(this.Model, metadataTypeReference, resourceValue, isOpenPropertyType, this.WriterValidator);

            Debug.Assert(
                metadataTypeReference == null || resourceValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, resourceValueTypeReference),
                "Complex property types must be the same as or inherit from the ones from metadata (unless open).");

            typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(resourceValue, metadataTypeReference, resourceValueTypeReference, isOpenPropertyType);
            if (typeName != null)
            {
                await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName)
                .ConfigureAwait(false);
            }

            // Write custom instance annotations
            await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(resourceValue.InstanceAnnotations)
            .ConfigureAwait(false);

            // Write the properties of the resource value as usual. Note we do not allow resource types to contain named stream properties.
            await this.PropertySerializer.WritePropertiesAsync(
                resourceValueTypeReference == null?null : resourceValueTypeReference.StructuredDefinition(),
                resourceValue.Properties,
                true /* isComplexValue */,
                duplicatePropertyNamesChecker,
                null).ConfigureAwait(false);

            // End the object scope which represents the resource instance;
            await this.AsynchronousJsonWriter.EndObjectScopeAsync().ConfigureAwait(false);

            this.DecreaseRecursionDepth();
        }
        /// <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="propertyInfo">The serialization info of current property</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,
            PropertySerializationInfo propertyInfo,
            bool isOpenProperty)
        {
            string fullTypeNameFromValue       = null;
            PropertyValueTypeInfo    valueType = propertyInfo.ValueType;
            PropertyMetadataTypeInfo modelType = propertyInfo.MetadataType;

            string typeNameToWrite;

            if (TypeNameOracle.TryGetTypeNameFromAnnotation(value, out typeNameToWrite))
            {
                return(typeNameToWrite);
            }

            if (valueType.TypeReference != null)
            {
                fullTypeNameFromValue = valueType.FullName;

                // Write type name when the type in the payload is more derived than the type from metadata.
                if (modelType.TypeReference != null && modelType.FullName != fullTypeNameFromValue)
                {
                    return(fullTypeNameFromValue);
                }

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

                // 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 (modelType.TypeReference == null && valueType.IsComplex)
                {
                    if ((valueType.TypeReference as IEdmComplexTypeReference).ComplexDefinition().BaseType != null)
                    {
                        return(fullTypeNameFromValue);
                    }
                }
            }

            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(fullTypeNameFromValue != null ? fullTypeNameFromValue : GetTypeNameFromValue(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)
        {
            string fullTypeNameFromValue = null;

            string typeNameToWrite;

            if (TypeNameOracle.TryGetTypeNameFromAnnotation(value, out typeNameToWrite))
            {
                return(typeNameToWrite);
            }

            if (typeReferenceFromValue != null)
            {
                fullTypeNameFromValue = typeReferenceFromValue.FullName();

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

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

                // 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(fullTypeNameFromValue);
                    }
                }
            }

            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(fullTypeNameFromValue != null ? fullTypeNameFromValue : GetTypeNameFromValue(value));
        }
        /// <summary>
        /// Writes out the value of a complex property.
        /// </summary>
        /// <param name="complexValue">The complex value to write.</param>
        /// <param name="propertyTypeReference">The metadata type for the complex value.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="collectionValidator">The collection validator instance to validate the type names and type kinds of collection items; null if no validation is needed.</param>
        /// <remarks>The current recursion depth should be a value, measured by the number of complex and collection values between
        /// this complex value and the top-level payload, not including this one.</remarks>
        internal void WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            this.IncreaseRecursionDepth();

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

            string typeName = complexValue.TypeName;

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

            // resolve the type name to the type; if no type name is specified we will use the type inferred from metadata
            IEdmComplexTypeReference complexValueTypeReference = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, complexValue, isOpenPropertyType).AsComplexOrNull();

            string collectionItemTypeName;

            typeName = this.VerboseJsonOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexValueTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName);
            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            // Write the "__metadata" : { "type": "typename" }
            // But only if we actually have a typename to write, otherwise we need the __metadata to be omitted entirely
            if (typeName != null)
            {
                ODataJsonWriterUtils.WriteMetadataWithTypeName(this.JsonWriter, typeName);
            }

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

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

            this.DecreaseRecursionDepth();
        }
        /// <summary>
        /// Writes a primitive property.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="primitiveValue">The primitive value to be written</param>
        /// <param name="propertyTypeReference">The metadata type reference of the property.</param>
        /// <param name="isTopLevel">true when writing a top-level property; false for nested properties.</param>
        /// <param name="isOpenPropertyType">If the property is open.</param>
        private void WritePrimitiveProperty(
            ODataProperty property,
            ODataPrimitiveValue primitiveValue,
            IEdmTypeReference propertyTypeReference,
            bool isTopLevel,
            bool isOpenPropertyType)
        {
            string wirePropertyName = GetWirePropertyName(isTopLevel, property.Name);

            IEdmTypeReference typeFromValue   = TypeNameOracle.ResolveAndValidateTypeForPrimitiveValue(primitiveValue);
            string            typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(primitiveValue, propertyTypeReference, typeFromValue, isOpenPropertyType);

            this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
            this.JsonWriter.WriteName(wirePropertyName);
            this.JsonLightValueSerializer.WritePrimitiveValue(primitiveValue.Value, propertyTypeReference);
        }
        /// <summary>
        /// Writes a collection property.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="collectionValue">The collection value to be written</param>
        /// <param name="propertyTypeReference">The metadata type reference of the property.</param>
        /// <param name="isTopLevel">true when writing a top-level property; false for nested properties.</param>
        /// <param name="isOpenPropertyType">If the property is open.</param>
        private void WriteCollectionProperty(
            ODataProperty property,
            ODataCollectionValue collectionValue,
            IEdmTypeReference propertyTypeReference,
            bool isTopLevel,
            bool isOpenPropertyType)
        {
            string wirePropertyName = GetWirePropertyName(isTopLevel, property.Name);

            IEdmTypeReference typeFromValue   = TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator);
            string            typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, propertyTypeReference, typeFromValue, isOpenPropertyType);

            this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
            this.JsonWriter.WriteName(wirePropertyName);

            // passing false for 'isTopLevel' because the outer wrapping object has already been written.
            this.JsonLightValueSerializer.WriteCollectionValue(collectionValue, propertyTypeReference, typeFromValue, isTopLevel, false /*isInUri*/, isOpenPropertyType);
            return;
        }
        /// <summary>
        /// Writes a enum property.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="enumValue">The enum value to be written.</param>
        /// <param name="propertyTypeReference">The metadata type reference of the property.</param>
        /// <param name="isTopLevel">true when writing a top-level property; false for nested properties.</param>
        /// <param name="isOpenPropertyType">If the property is open.</param>
        private void WriteEnumProperty(
            ODataProperty property,
            ODataEnumValue enumValue,
            IEdmTypeReference propertyTypeReference,
            bool isTopLevel,
            bool isOpenPropertyType)
        {
            string wirePropertyName = GetWirePropertyName(isTopLevel, property.Name);

            IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeForEnumValue(this.Model, enumValue, isOpenPropertyType);

            // This is a work around, needTypeOnWire always = true for client side:
            // ClientEdmModel's reflection can't know a property is open type even if it is, so here
            // make client side always write 'odata.type' for enum.
            bool   needTypeOnWire  = string.Equals(this.JsonLightOutputContext.Model.GetType().Name, "ClientEdmModel", StringComparison.OrdinalIgnoreCase);
            string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(
                enumValue, propertyTypeReference, typeFromValue, needTypeOnWire || isOpenPropertyType);

            this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
            this.JsonWriter.WriteName(wirePropertyName);
            this.JsonLightValueSerializer.WriteEnumValue(enumValue, propertyTypeReference);
        }
Esempio n. 11
0
        /// <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)
        {
            string typeNameToWrite;

            if (TypeNameOracle.TryGetTypeNameFromAnnotation(value, out typeNameToWrite))
            {
                return(typeNameToWrite);
            }

            // 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));
        }
Esempio n. 12
0
        private void ResolvePrimitiveValueTypeName(
            ODataPrimitiveValue primitiveValue,
            bool isOpenPropertyType)
        {
            string typeName = primitiveValue.Value.GetType().Name;

            if (this.currentPropertyInfo.ValueType == null || this.currentPropertyInfo.ValueType.TypeName != typeName)
            {
                IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeForPrimitiveValue(primitiveValue);

                this.currentPropertyInfo.ValueType       = new PropertyValueTypeInfo(typeName, typeFromValue);
                this.currentPropertyInfo.TypeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(primitiveValue,
                                                                                                                                 this.currentPropertyInfo, isOpenPropertyType);
            }
            else
            {
                string typeNameToWrite;
                if (TypeNameOracle.TryGetTypeNameFromAnnotation(primitiveValue, out typeNameToWrite))
                {
                    this.currentPropertyInfo.TypeNameToWrite = typeNameToWrite;
                }
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Reads a property value starting with the specified index to the property value path.
        /// </summary>
        /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param>
        /// <param name="cachedProperties">The enumeration of properties to search for the first property in the property value path.</param>
        /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param>
        /// <param name="structuredTypeReference">The type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param>
        /// <param name="epmValueCache">The EPM value cache to use.</param>
        /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
        private object ReadPropertyValue(
            EntityPropertyMappingInfo epmInfo,
            IEnumerable <ODataProperty> cachedProperties,
            int sourceSegmentIndex,
            IEdmStructuredTypeReference structuredTypeReference,
            EpmValueCache epmValueCache)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now.");
            Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index.");
            Debug.Assert(structuredTypeReference != null, "structuredTypeReference != null");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");

            EpmSourcePathSegment sourceSegment = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string propertyName = sourceSegment.PropertyName;
            bool   lastSegment  = epmInfo.PropertyValuePath.Length == sourceSegmentIndex + 1;

            IEdmStructuredType structuredType        = structuredTypeReference.StructuredDefinition();
            IEdmProperty       edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, structuredType, this.atomOutputContext.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            IEdmTypeReference  propertyTypeReference = null;

            if (edmProperty != null)
            {
                propertyTypeReference = edmProperty.Type;

                // If this is the last part of the path, then it has to be a primitive or atomic collection type otherwise should be a complex type
                if (lastSegment)
                {
                    if (!propertyTypeReference.IsODataPrimitiveTypeKind() && !propertyTypeReference.IsNonEntityCollectionType())
                    {
                        throw new ODataException(ODataErrorStrings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else
                {
                    if (propertyTypeReference.TypeKind() != EdmTypeKind.Complex)
                    {
                        throw new ODataException(ODataErrorStrings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                    }
                }
            }
            else
            {
                Debug.Assert(
                    structuredType.IsOpen,
                    "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method.");
            }

            ODataProperty property = cachedProperties == null ? null : cachedProperties.FirstOrDefault(p => p.Name == propertyName);

            if (property == null)
            {
                throw new ODataException(ODataErrorStrings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }

            object            propertyValue        = property.Value;
            ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue;

            if (lastSegment)
            {
                if (propertyValue == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.WriterBehavior, this.atomOutputContext.Model);
                }
                else
                {
                    // If this property is the last one it has to be either a primitive or collection
                    if (propertyComplexValue != null)
                    {
                        throw new ODataException(ODataErrorStrings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                    else
                    {
                        ODataCollectionValue propertyCollectionValue = propertyValue as ODataCollectionValue;
                        if (propertyCollectionValue != null)
                        {
                            // Validate the type name for the collection
                            TypeNameOracle.ResolveAndValidateTypeNameForValue(
                                this.atomOutputContext.Model,
                                propertyTypeReference,
                                propertyCollectionValue,
                                /*isOpenProperty*/ edmProperty == null);
                        }
                        else
                        {
                            if (propertyValue is ODataStreamReferenceValue)
                            {
                                // Stream properties should not come here, if it were an ODataEntry property it would have been
                                // filtered in ReadEntryPropertyValue() by "epmValueCache.EntryProperties" call.
                                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                            }
                            else if (propertyValue is ISpatial)
                            {
                                throw new ODataException(ODataErrorStrings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                            }
                            else if (propertyTypeReference != null)
                            {
                                ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, propertyTypeReference);
                            }
                        }
                    }
                }

                return(propertyValue);
            }

            // Otherwise it's in the middle and thus it must be a complex value
            if (propertyComplexValue == null)
            {
                if (propertyValue != null)
                {
                    // It's not a complex value - fail.
                    throw new ODataException(ODataErrorStrings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                else
                {
                    // The value of the property is null, which can be a null complex value
                    // Note that we must not attempt to resolve the type as if the type name was null here, because
                    //  1) We don't need the type for anything anyway (the value is null, this is the end)
                    //  2) If the property is open, trying to resolve a null type name would throw
                    //     but we don't have a null type name, we have a null entire value.
                    return(null);
                }
            }

            IEdmComplexTypeReference complexValueType = TypeNameOracle.ResolveAndValidateTypeNameForValue(
                this.atomOutputContext.Model,
                edmProperty == null ? null : edmProperty.Type,
                propertyComplexValue,
                /*isOpenProperty*/ edmProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(
                       epmInfo,
                       propertyComplexValue,
                       epmValueCache,
                       sourceSegmentIndex + 1,
                       complexValueType));
        }
Esempio n. 14
0
        public void WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference metadataTypeReference,
            bool isTopLevel,
            bool isOpenPropertyType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            Debug.Assert(complexValue != null, "complexValue != null");

            this.IncreaseRecursionDepth();

            // Start the object scope which will represent the entire complex instance;
            // for top-level complex properties we already wrote the object scope (and the context URI when needed).
            if (!isTopLevel)
            {
                this.JsonWriter.StartObjectScope();
            }

            string typeName = complexValue.TypeName;

            if (isTopLevel)
            {
                Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties.");
                if (typeName == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnComplex);
                }
            }
            else
            {
                // In requests, we allow the property type reference to be null if the type name is specified in the OM
                if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel())
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForComplexValueRequest);
                }
            }

            // Resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata.
            IEdmComplexTypeReference complexValueTypeReference = (IEdmComplexTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType);

            Debug.Assert(
                metadataTypeReference == null || complexValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, complexValueTypeReference),
                "Complex property types must be the same as or inherit from the ones from metadata (unless open).");

            typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, metadataTypeReference, complexValueTypeReference, isOpenPropertyType);
            if (typeName != null)
            {
                ODataJsonLightWriterUtils.WriteODataTypeInstanceAnnotation(this.JsonWriter, typeName);
            }

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

            // End the object scope which represents the complex instance;
            // for top-level complex properties we already wrote the end object scope.
            if (!isTopLevel)
            {
                this.JsonWriter.EndObjectScope();
            }

            this.DecreaseRecursionDepth();
        }
Esempio n. 15
0
        /// <summary>
        /// Write the items of a collection in ATOM format.
        /// </summary>
        /// <param name="collectionValue">The collection value to write.</param>
        /// <param name="propertyTypeReference">The type reference of the collection value (or null if not metadata is available).</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        private void WriteCollectionValue(
            ODataCollectionValue collectionValue,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection)
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType);

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName);

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

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

            CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName);

            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, false /* isStreamable */);

                    this.XmlWriter.WriteStartElement(AtomConstants.ODataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, this.MessageWriterSettings.WriterBehavior.ODataNamespace);
                    ODataComplexValue complexValue = item as ODataComplexValue;
                    if (complexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            complexValue,
                            expectedItemTypeReference,
                            false,
                            isWritingCollection,
                            null /* beforeValueAction */,
                            null /* afterValueAction */,
                            duplicatePropertyNamesChecker,
                            collectionValidator,
                            null /* epmValueCache */,
                            null /* epmSourcePathSegment */,
                            null /* projectedProperties */);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        // Note: Currently there is no way for a user to control primitive type information when the primitive values are part of a collection.
                        this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null);
                    }

                    this.XmlWriter.WriteEndElement();
                }
            }

            this.DecreaseRecursionDepth();
        }
Esempio n. 16
0
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            WriterValidationUtils.ValidatePropertyNotNull(property);

            string propertyName = property.Name;

            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty      edmProperty           = null;
            IEdmTypeReference propertyTypeReference = null;

            if (this.JsonLightOutputContext.MessageWriterSettings.EnableFullValidation)
            {
                edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);
                propertyTypeReference = edmProperty == null ? null : edmProperty.Type;
            }

            ODataValue          value          = property.ODataValue;
            ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue;

            ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;

            if (streamReferenceValue != null)
            {
                if (!allowStreamProperty)
                {
                    throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }

                Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                Debug.Assert(!isTopLevel, "Stream properties are not allowed at the top level.");
                WriterValidationUtils.ValidateStreamReferenceProperty(property, edmProperty, this.Version, this.WritingResponse);
                this.WriteStreamReferenceProperty(propertyName, streamReferenceValue);
                return;
            }

            string wirePropertyName = isTopLevel ? JsonLightConstants.ODataValuePropertyName : propertyName;

            if (value is ODataNullValue || value == null)
            {
                WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.MessageWriterSettings.WriterBehavior, this.Model);

                if (isTopLevel)
                {
                    // Write the special null marker for top-level null properties.
                    this.JsonWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataNull);
                    this.JsonWriter.WriteValue(true);
                }
                else
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                    this.JsonLightValueSerializer.WriteNullValue();
                }

                return;
            }

            bool isOpenPropertyType = this.IsOpenProperty(property, owningType, edmProperty);

            if (isOpenPropertyType && this.JsonLightOutputContext.MessageWriterSettings.EnableFullValidation)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value);
            }

            ODataComplexValue complexValue = value as ODataComplexValue;

            if (complexValue != null)
            {
                if (!isTopLevel)
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                }

                this.JsonLightValueSerializer.WriteComplexValue(complexValue, propertyTypeReference, isTopLevel, isOpenPropertyType, this.CreateDuplicatePropertyNamesChecker());
                return;
            }

            IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, value, isOpenPropertyType);
            ODataEnumValue    enumValue     = value as ODataEnumValue;

            if (enumValue != null)
            {
                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(enumValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
                this.JsonWriter.WriteName(wirePropertyName);
                this.JsonLightValueSerializer.WriteEnumValue(enumValue, propertyTypeReference);
                return;
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                string collectionTypeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, collectionTypeNameToWrite, isTopLevel);
                this.JsonWriter.WriteName(wirePropertyName);

                // passing false for 'isTopLevel' because the outer wrapping object has already been written.
                this.JsonLightValueSerializer.WriteCollectionValue(collectionValue, propertyTypeReference, isTopLevel, false /*isInUri*/, isOpenPropertyType);
            }
            else
            {
                Debug.Assert(primitiveValue != null, "primitiveValue != null");

                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(primitiveValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);

                this.JsonWriter.WriteName(wirePropertyName);
                this.JsonLightValueSerializer.WritePrimitiveValue(primitiveValue.Value, propertyTypeReference);
            }
        }
        /// <summary>
        /// Asynchronously writes out the value of a collection property.
        /// </summary>
        /// <param name="collectionValue">The collection value to write.</param>
        /// <param name="metadataTypeReference">The metadata type reference for the collection.</param>
        /// <param name="valueTypeReference">The value type reference for the collection.</param>
        /// <param name="isTopLevelProperty">Whether or not a top-level property is being written.</param>
        /// <param name="isInUri">Whether or not the value is being written for a URI.</param>
        /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param>
        /// <remarks>The current recursion depth is measured by the number of resource and collection values between
        /// this one and the top-level payload, not including this one.</remarks>
        /// <returns>A task that represents the asynchronous write operation.</returns>
        public virtual async Task WriteCollectionValueAsync(
            ODataCollectionValue collectionValue,
            IEdmTypeReference metadataTypeReference,
            IEdmTypeReference valueTypeReference,
            bool isTopLevelProperty,
            bool isInUri,
            bool isOpenPropertyType)
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");
            Debug.Assert(!isTopLevelProperty || !isInUri, "Cannot be a top level property and in a uri");

            this.IncreaseRecursionDepth();

            // If the CollectionValue has type information write out the metadata and the type in it.
            string typeName = collectionValue.TypeName;

            if (isTopLevelProperty)
            {
                Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties.");
                if (typeName == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnCollection);
                }
            }
            else
            {
                // In requests, we allow the metadata type reference to be null if the type name is specified in the OM
                if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel())
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForCollectionValueInRequest);
                }
            }

            if (valueTypeReference == null)
            {
                valueTypeReference = TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, metadataTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator);
            }

            bool useValueProperty = false;

            if (isInUri)
            {
                // resolve the type name to the type; if no type name is specified we will use the
                // type inferred from metadata
                typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, metadataTypeReference, valueTypeReference, isOpenPropertyType);
                if (!string.IsNullOrEmpty(typeName))
                {
                    useValueProperty = true;

                    // "{"
                    await this.AsynchronousJsonWriter.StartObjectScopeAsync().ConfigureAwait(false);

                    await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName)
                    .ConfigureAwait(false);

                    await this.AsynchronousJsonWriter.WriteValuePropertyNameAsync().ConfigureAwait(false);
                }
            }

            // [
            // This represents the array of items in the CollectionValue
            await this.AsynchronousJsonWriter.StartArrayScopeAsync().ConfigureAwait(false);

            // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration)
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                IEdmTypeReference expectedItemTypeReference = valueTypeReference == null ? null : ((IEdmCollectionTypeReference)valueTypeReference).ElementType();

                IDuplicatePropertyNameChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable());

                    ODataResourceValue itemAsResourceValue = item as ODataResourceValue;
                    if (itemAsResourceValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.GetDuplicatePropertyNameChecker();
                        }

                        await this.WriteResourceValueAsync(
                            itemAsResourceValue,
                            expectedItemTypeReference,
                            false /*isOpenPropertyType*/,
                            duplicatePropertyNamesChecker).ConfigureAwait(false);

                        duplicatePropertyNamesChecker.Reset();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        // by design: collection element's type name is not written for enum or non-spatial primitive value even in case of full metadata.
                        // because enum and non-spatial primitive types don't have inheritance, the type of each element is the same as the item type of the collection, whose type name for spatial types in full metadata mode.
                        ODataEnumValue enumValue = item as ODataEnumValue;
                        if (enumValue != null)
                        {
                            await this.WriteEnumValueAsync(enumValue, expectedItemTypeReference).ConfigureAwait(false);
                        }
                        else
                        {
                            ODataUntypedValue untypedValue = item as ODataUntypedValue;
                            if (untypedValue != null)
                            {
                                await this.WriteUntypedValueAsync(untypedValue).ConfigureAwait(false);
                            }
                            else if (item != null)
                            {
                                await this.WritePrimitiveValueAsync(item, expectedItemTypeReference).ConfigureAwait(false);
                            }
                            else
                            {
                                await this.WriteNullValueAsync().ConfigureAwait(false);
                            }
                        }
                    }
                }

                if (duplicatePropertyNamesChecker != null)
                {
                    this.ReturnDuplicatePropertyNameChecker(duplicatePropertyNamesChecker);
                }
            }

            // End the array scope which holds the items
            await this.AsynchronousJsonWriter.EndArrayScopeAsync().ConfigureAwait(false);

            if (useValueProperty)
            {
                await this.AsynchronousJsonWriter.EndObjectScopeAsync().ConfigureAwait(false);
            }

            this.DecreaseRecursionDepth();
        }
Esempio n. 18
0
        internal bool WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection,
            Action beforeValueAction,
            Action afterValueAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(complexValue != null, "complexValue != null");

            string typeName = complexValue.TypeName;

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

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType).AsComplexOrNull();

            string collectionItemTypeName;

            typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName);
            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            Action beforeValueCallbackWithTypeName = beforeValueAction;

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

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

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

                            keepInContentPrimitiveProperties.Add(edmProperty.Name);
                        }
                    }

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

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

            this.DecreaseRecursionDepth();
            return(propertyWritten);
        }
Esempio n. 19
0
        /// <summary>
        /// Write the items of a collection in ATOM format.
        /// </summary>
        /// <param name="collectionValue">The collection value to write.</param>
        /// <param name="propertyTypeReference">The type reference of the collection value (or null if not metadata is available).</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        private void WriteCollectionValue(
            ODataCollectionValue collectionValue,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection)
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator);

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName);

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

            // COMPAT 4: ATOM and JSON format for collections - The collection format is not formally specified yet, since it's a new feature
            // If the official format deviates from the current WCFDS behavior we would have to introduce a back compat mode here as well.
            IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

            CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName);

            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable());

                    this.XmlWriter.WriteStartElement(AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, AtomConstants.ODataMetadataNamespace);
                    ODataComplexValue complexValue = item as ODataComplexValue;
                    if (complexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            complexValue,
                            expectedItemTypeReference,
                            false,
                            isWritingCollection,
                            null /* beforeValueAction */,
                            null /* afterValueAction */,
                            duplicatePropertyNamesChecker,
                            collectionValidator,
                            null /* projectedProperties */);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        ODataEnumValue enumValue = item as ODataEnumValue;
                        if (enumValue != null)
                        {
                            // Note: Currently there is no way for a user to control enum type's serializationTypeNameAnnotation information when the enum values are part of a collection.
                            this.WriteEnumValue(enumValue, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null);
                        }
                        else
                        {
                            // Note: Currently there is no way for a user to control primitive type information when the primitive values are part of a collection.
                            if (item != null)
                            {
                                this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null);
                            }
                            else
                            {
                                this.WriteNullCollectionElementValue(expectedItemTypeReference);
                            }
                        }
                    }

                    this.XmlWriter.WriteEndElement();
                }
            }

            this.DecreaseRecursionDepth();
        }
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);

            string propertyName = property.Name;

            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            bool         shouldWriteRawAnnotations = false;

            if (!ShouldWriteProperty(owningType, property, edmProperty, out shouldWriteRawAnnotations))
            {
                return;
            }

            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;
            bool alreadyWroteODataType = false;

            if (shouldWriteRawAnnotations)
            {
                TryWriteRawAnnotations(property, out alreadyWroteODataType);
            }

            // handle ODataUntypedValue
            ODataUntypedValue untypedValue = property.Value as ODataUntypedValue;

            if (untypedValue != null)
            {
                if (this.MessageWriterSettings.ContainUndeclaredPropertyBehavior(
                        ODataUndeclaredPropertyBehaviorKinds.SupportUndeclaredValueProperty))
                {
                    this.JsonWriter.WriteName(propertyName);
                    this.JsonWriter.WriteRawString(untypedValue.RawJson);
                }

                return;
            }

            ODataValue          value          = property.ODataValue;
            ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue;

            // If the property is of Geography or Geometry type or the value is of Geography or Geometry type
            // make sure to check that the version is 3.0 or above.
            bool isSpatial = (propertyTypeReference != null && propertyTypeReference.IsSpatial()) || (propertyTypeReference == null && primitiveValue != null && primitiveValue.Value is ISpatial);

            if (isSpatial)
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;

            if (streamReferenceValue != null)
            {
                if (!allowStreamProperty)
                {
                    throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }

                Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                Debug.Assert(!isTopLevel, "Stream properties are not allowed at the top level.");
                WriterValidationUtils.ValidateStreamReferenceProperty(property, edmProperty, this.Version, this.WritingResponse);
                this.WriteStreamReferenceProperty(propertyName, streamReferenceValue);
                return;
            }

            string wirePropertyName = isTopLevel ? JsonLightConstants.ODataValuePropertyName : propertyName;

            if (value is ODataNullValue || value == null)
            {
                WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.MessageWriterSettings.WriterBehavior, this.Model);

                if (isTopLevel)
                {
                    // Write the special null marker for top-level null properties.
                    this.JsonWriter.WriteName(ODataAnnotationNames.ODataNull);
                    this.JsonWriter.WriteValue(true);
                }
                else
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                    this.JsonLightValueSerializer.WriteNullValue();
                }

                return;
            }

            bool isOpenPropertyType = IsOpenPropertyType(property, owningType, edmProperty);

            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            }

            ODataComplexValue complexValue = value as ODataComplexValue;

            if (complexValue != null)
            {
                if (!isTopLevel)
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                }

                this.JsonLightValueSerializer.WriteComplexValue(complexValue, propertyTypeReference, isTopLevel, isOpenPropertyType, this.CreateDuplicatePropertyNamesChecker());
                return;
            }

            IEdmTypeReference    typeFromValue   = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, value, isOpenPropertyType);
            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                if (!alreadyWroteODataType)
                {
                    string collectionTypeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                    this.WritePropertyTypeName(wirePropertyName, collectionTypeNameToWrite, isTopLevel);
                }

                this.JsonWriter.WriteName(wirePropertyName);
                ODataVersionChecker.CheckCollectionValueProperties(this.Version, propertyName);

                // passing false for 'isTopLevel' because the outer wrapping object has already been written.
                this.JsonLightValueSerializer.WriteCollectionValue(collectionValue, propertyTypeReference, isTopLevel, false /*isInUri*/, isOpenPropertyType);
                return;
            }

            Debug.Assert(primitiveValue != null, "primitiveValue != null");

            if (!alreadyWroteODataType)
            {
                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(primitiveValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
            }

            this.JsonWriter.WriteName(wirePropertyName);
            this.JsonLightValueSerializer.WritePrimitiveValue(primitiveValue.Value, propertyTypeReference);
        }
Esempio n. 21
0
        internal bool WriteComplexValue(
            ODataComplexValue complexValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection,
            Action beforeValueAction,
            Action afterValueAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            CollectionWithoutExpectedTypeValidator collectionValidator,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            Debug.Assert(complexValue != null, "complexValue != null");

            string typeName = complexValue.TypeName;

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

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeForComplexValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType, this.WriterValidator).AsComplexOrNull();

            string collectionItemTypeName;

            typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName);
            Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null");

            Action beforeValueCallbackWithTypeName = beforeValueAction;

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

            bool propertyWritten = this.WriteProperties(
                complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(),
                complexValue.Properties,
                isWritingCollection,
                beforeValueCallbackWithTypeName,
                afterValueAction,
                duplicatePropertyNamesChecker,
                projectedProperties);

            this.DecreaseRecursionDepth();
            return(propertyWritten);
        }
Esempio n. 22
0
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            WriterValidationUtils.ValidatePropertyNotNull(property);

            string propertyName = property.Name;

            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            WriterValidationUtils.ValidatePropertyName(propertyName, bypassValidation);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);

            if (property.InstanceAnnotations.Any())
            {
                if (isTopLevel)
                {
                    this.InstanceAnnotationWriter.WriteInstanceAnnotations(property.InstanceAnnotations);
                }
                else
                {
                    this.InstanceAnnotationWriter.WriteInstanceAnnotations(property.InstanceAnnotations, propertyName);
                }
            }

            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(
                propertyName,
                owningType,
                !this.bypassValidation);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            ODataValue value = property.ODataValue;

            ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;

            if (streamReferenceValue != null)
            {
                if (!allowStreamProperty)
                {
                    throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }

                Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                Debug.Assert(!isTopLevel, "Stream properties are not allowed at the top level.");
                WriterValidationUtils.ValidateStreamReferenceProperty(property, edmProperty, this.WritingResponse, this.bypassValidation);
                this.WriteStreamReferenceProperty(propertyName, streamReferenceValue);
                return;
            }

            string wirePropertyName = isTopLevel ? JsonLightConstants.ODataValuePropertyName : propertyName;

            if (value is ODataNullValue || value == null)
            {
                WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.MessageWriterSettings.WriterBehavior, this.Model, this.bypassValidation);

                if (isTopLevel)
                {
                    // Write the special null marker for top-level null properties.
                    this.JsonWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataNull);
                    this.JsonWriter.WriteValue(true);
                }
                else
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                    this.JsonLightValueSerializer.WriteNullValue();
                }

                return;
            }

            bool isOpenPropertyType = this.IsOpenProperty(property, owningType, edmProperty);

            if (isOpenPropertyType && this.JsonLightOutputContext.MessageWriterSettings.EnableFullValidation)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value);
            }

            ODataComplexValue complexValue = value as ODataComplexValue;

            if (complexValue != null)
            {
                if (!isTopLevel)
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                }

                this.JsonLightValueSerializer.WriteComplexValue(complexValue, propertyTypeReference, isTopLevel, isOpenPropertyType, this.CreateDuplicatePropertyNamesChecker());
                return;
            }

            IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, value, isOpenPropertyType);
            ODataEnumValue    enumValue     = value as ODataEnumValue;

            if (enumValue != null)
            {
                // This is a work around, needTypeOnWire always = true for client side:
                // ClientEdmModel's reflection can't know a property is open type even if it is, so here
                // make client side always write 'odata.type' for enum.
                bool   needTypeOnWire  = string.Equals(this.JsonLightOutputContext.Model.GetType().Name, "ClientEdmModel", StringComparison.OrdinalIgnoreCase);
                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(
                    enumValue, propertyTypeReference, typeFromValue, needTypeOnWire ? true /* leverage this flag to write 'odata.type' */ : isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
                this.JsonWriter.WriteName(wirePropertyName);
                this.JsonLightValueSerializer.WriteEnumValue(enumValue, propertyTypeReference);
                return;
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                string collectionTypeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, collectionTypeNameToWrite, isTopLevel);
                this.JsonWriter.WriteName(wirePropertyName);

                // passing false for 'isTopLevel' because the outer wrapping object has already been written.
                this.JsonLightValueSerializer.WriteCollectionValue(collectionValue, propertyTypeReference, isTopLevel, false /*isInUri*/, isOpenPropertyType);
            }
            else
            {
                ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue;
                Debug.Assert(primitiveValue != null, "primitiveValue != null");

                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(primitiveValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);

                this.JsonWriter.WriteName(wirePropertyName);
                this.JsonLightValueSerializer.WritePrimitiveValue(primitiveValue.Value, propertyTypeReference);
            }
        }
Esempio n. 23
0
        /// <summary>
        /// Verifies that the given <paramref name="primitiveValue"/> is or can be coerced to <paramref name="expectedTypeReference"/>, and coerces it if necessary.
        /// </summary>
        /// <param name="primitiveValue">An EDM primitive value to verify.</param>
        /// <param name="model">Model to verify against.</param>
        /// <param name="expectedTypeReference">Expected type reference.</param>
        /// <param name="version">The version to use for reading.</param>
        /// <returns>Coerced version of the <paramref name="primitiveValue"/>.</returns>
        internal static object VerifyAndCoerceUriPrimitiveLiteral(object primitiveValue, IEdmModel model, IEdmTypeReference expectedTypeReference, ODataVersion version)
        {
            DebugUtils.CheckNoExternalCallers();
            ExceptionUtils.CheckArgumentNotNull(primitiveValue, "primitiveValue");
            ExceptionUtils.CheckArgumentNotNull(model, "model");
            ExceptionUtils.CheckArgumentNotNull(expectedTypeReference, "expectedTypeReference");

            // First deal with null literal
            ODataUriNullValue nullValue = primitiveValue as ODataUriNullValue;

            if (nullValue != null)
            {
                if (!expectedTypeReference.IsNullable)
                {
                    throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralNullOnNonNullableType(expectedTypeReference.ODataFullName()));
                }

                // If it was just 'null' on the wire (no typed null) then just take what was expected and use that
                if (nullValue.TypeName == null)
                {
                    nullValue.TypeName = expectedTypeReference.ODataFullName();
                    return(nullValue);
                }

                IEdmType actualResolvedType = TypeNameOracle.ResolveAndValidateTypeName(model, nullValue.TypeName, expectedTypeReference.Definition.TypeKind);
                Debug.Assert(actualResolvedType != null, "This is a primitive-only codepath so actualResolvedType != null.");

                if (actualResolvedType.IsSpatial())
                {
                    ODataVersionChecker.CheckSpatialValue(version);
                }

                if (TypePromotionUtils.CanConvertTo(actualResolvedType.ToTypeReference(), expectedTypeReference))
                {
                    nullValue.TypeName = expectedTypeReference.ODataFullName();
                    return(nullValue);
                }

                throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralNullTypeVerificationFailure(expectedTypeReference.ODataFullName(), nullValue.TypeName));
            }

            // Only other positive case is a numeric primitive that needs to be coerced
            IEdmPrimitiveTypeReference expectedPrimitiveTypeReference = expectedTypeReference.AsPrimitiveOrNull();

            if (expectedPrimitiveTypeReference == null)
            {
                throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralTypeVerificationFailure(expectedTypeReference.ODataFullName(), primitiveValue));
            }

            object coercedResult = CoerceNumericType(primitiveValue, expectedPrimitiveTypeReference.PrimitiveDefinition());

            if (coercedResult != null)
            {
                return(coercedResult);
            }

            Type actualType = primitiveValue.GetType();
            Type targetType = TypeUtils.GetNonNullableType(EdmLibraryExtensions.GetPrimitiveClrType(expectedPrimitiveTypeReference));

            // If target type is assignable from actual type, we're OK
            if (targetType.IsAssignableFrom(actualType))
            {
                return(primitiveValue);
            }

            throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralTypeVerificationFailure(expectedPrimitiveTypeReference.ODataFullName(), primitiveValue));
        }
        public void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isTopLevelProperty, bool isInUri, bool isOpenPropertyType)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(collectionValue != null, "collectionValue != null");
            Debug.Assert(!isTopLevelProperty || !isInUri, "Cannot be a top level property and in a uri");

            this.IncreaseRecursionDepth();

            // If the CollectionValue has type information write out the metadata and the type in it.
            string typeName = collectionValue.TypeName;

            if (isTopLevelProperty)
            {
                Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties.");
                if (typeName == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnCollection);
                }
            }
            else
            {
                // In requests, we allow the metadata type reference to be null if the type name is specified in the OM
                if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel())
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForCollectionValueInRequest);
                }
            }

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, collectionValue, isOpenPropertyType);

            typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, metadataTypeReference, collectionTypeReference, isOpenPropertyType);
            bool useValueProperty = isInUri && !string.IsNullOrEmpty(typeName);

            if (useValueProperty)
            {
                // "{"
                this.JsonWriter.StartObjectScope();
                ODataJsonLightWriterUtils.WriteODataTypeInstanceAnnotation(this.JsonWriter, typeName);
                this.JsonWriter.WriteValuePropertyName();
            }

            // [
            // This represents the array of items in the CollectionValue
            this.JsonWriter.StartArrayScope();

            // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration)
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, false /* isStreamable */);

                    ODataComplexValue itemAsComplexValue = item as ODataComplexValue;
                    if (itemAsComplexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            itemAsComplexValue,
                            expectedItemTypeReference,
                            false /*isTopLevel*/,
                            false /*isOpenPropertyType*/,
                            duplicatePropertyNamesChecker);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        this.WritePrimitiveValue(item, expectedItemTypeReference);
                    }
                }
            }

            // End the array scope which holds the items
            this.JsonWriter.EndArrayScope();

            if (useValueProperty)
            {
                this.JsonWriter.EndObjectScope();
            }

            this.DecreaseRecursionDepth();
        }
        /// <summary>
        /// Writes out the value of a collection property.
        /// </summary>
        /// <param name="collectionValue">The collection value to write.</param>
        /// <param name="metadataTypeReference">The metadata type reference for the collection.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <remarks>The current recursion depth is measured by the number of complex and collection values between
        /// this one and the top-level payload, not including this one.</remarks>
        internal void WriteCollectionValue(
            ODataCollectionValue collectionValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(collectionValue != null, "collectionValue != null");

            this.IncreaseRecursionDepth();

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

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, collectionValue, isOpenPropertyType);

            // "__metadata": { "type": "typename" }
            // If the CollectionValue has type information write out the metadata and the type in it.
            string collectionItemTypeName;
            string typeName = this.VerboseJsonOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName);

            if (typeName != null)
            {
                ODataJsonWriterUtils.WriteMetadataWithTypeName(this.JsonWriter, typeName);
            }

            // "results": [
            // This represents the array of items in the CollectionValue
            this.JsonWriter.WriteDataArrayName();
            this.JsonWriter.StartArrayScope();

            // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration)
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName);

                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, false /* isStreamable */);

                    ODataComplexValue itemAsComplexValue = item as ODataComplexValue;
                    if (itemAsComplexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            itemAsComplexValue,
                            expectedItemTypeReference,
                            false,
                            duplicatePropertyNamesChecker,
                            collectionValidator);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference);
                    }
                }
            }

            // End the array scope which holds the items
            this.JsonWriter.EndArrayScope();

            // End the object scope which holds the entire collection
            this.JsonWriter.EndObjectScope();

            this.DecreaseRecursionDepth();
        }