/// <summary>
        /// Write the metadata for an OData association link; makes sure any duplicate of the link's values duplicated in metadata are equal.
        /// </summary>
        /// <param name="associationLink">The association link for which to write the metadata.</param>
        /// <param name="owningType">The <see cref="IEdmEntityType"/> instance the association link is defined on.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        internal void WriteAssociationLink(
            ODataAssociationLink associationLink,
            IEdmEntityType owningType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            ValidationUtils.ValidateAssociationLinkNotNull(associationLink);
            string associationLinkName = associationLink.Name;

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

            this.ValidateAssociationLink(associationLink, owningType);
            duplicatePropertyNamesChecker.CheckForDuplicateAssociationLinkNames(associationLink);

            AtomLinkMetadata linkMetadata       = associationLink.GetAnnotation <AtomLinkMetadata>();
            string           linkRelation       = AtomUtils.ComputeODataAssociationLinkRelation(associationLink);
            AtomLinkMetadata mergedLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(linkMetadata, linkRelation, associationLink.Url, associationLinkName, MimeConstants.MimeApplicationXml);

            this.atomEntryMetadataSerializer.WriteAtomLink(mergedLinkMetadata, null /* etag*/);
        }
        /// <summary>
        /// Writes property names and value pairs.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="properties">The enumeration of properties to write out.</param>
        /// <param name="isComplexValue">
        /// Whether the properties are being written for complex value. Also used for detecting whether stream properties
        /// are allowed as named stream properties should only be defined on ODataEntry instances
        /// </param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        internal void WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable <ODataProperty> properties,
            bool isComplexValue,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            if (properties == null)
            {
                return;
            }

            foreach (ODataProperty property in properties)
            {
                this.WriteProperty(
                    property,
                    owningType,
                    false /* isTopLevel */,
                    !isComplexValue,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Write the given collection of properties.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="cachedProperties">Collection of cached properties for the entry.</param>
        /// <param name="isWritingCollection">true if we are writing a top level collection instead of an entry.</param>
        /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param>
        /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        internal bool WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable <ODataProperty> cachedProperties,
            bool isWritingCollection,
            Action beforePropertiesAction,
            Action afterPropertiesAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            if (cachedProperties == null)
            {
                return(false);
            }

            bool propertyWritten = false;

            foreach (ODataProperty property in cachedProperties)
            {
                propertyWritten |= this.WriteProperty(
                    property,
                    owningType,
                    /*isTopLevel*/ false,
                    isWritingCollection,
                    propertyWritten ? null : beforePropertiesAction,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }

            if (afterPropertiesAction != null && propertyWritten)
            {
                afterPropertiesAction();
            }

            return(propertyWritten);
        }
Esempio n. 4
0
        /// <summary>
        /// Finish writing an entry.
        /// </summary>
        /// <param name="entry">The entry to write.</param>
        protected override void EndEntry(ODataEntry entry)
        {
            if (entry == null)
            {
                Debug.Assert(
                    this.ParentNavigationLink != null && !this.ParentNavigationLink.IsCollection.Value,
                    "when entry == null, it has to be and expanded single entry navigation");

                // this is a null expanded single entry and it is null, JSON null should be written as value in StartEntry()
                return;
            }

            // Get the projected properties
            ProjectedPropertiesAnnotation projectedProperties = GetProjectedPropertiesAnnotation(this.CurrentScope);

            // Write the properties
            this.verboseJsonEntryAndFeedSerializer.AssertRecursionDepthIsZero();
            this.verboseJsonEntryAndFeedSerializer.WriteProperties(
                this.EntryEntityType,
                entry.Properties,
                false /* isComplexValue */,
                this.DuplicatePropertyNamesChecker,
                projectedProperties);
            this.verboseJsonEntryAndFeedSerializer.AssertRecursionDepthIsZero();

            // Close the object scope
            this.verboseJsonOutputContext.JsonWriter.EndObjectScope();
        }
Esempio n. 5
0
        /// <summary>
        /// Determines if the property with the specified value should be written into content or not.
        /// </summary>
        /// <param name="owningType">The owning type of the property to be checked.</param>
        /// <param name="projectedProperties">The set of projected properties for the <paramref name="owningType"/></param>
        /// <param name="propertyName">The name of the property to be checked.</param>
        /// <param name="propertyValue">The property value to write.</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment for the property being written.</param>
        /// <returns>true if the property should be written into content, or false otherwise</returns>
        private bool ShouldWritePropertyInContent(
            IEdmStructuredType owningType,
            ProjectedPropertiesAnnotation projectedProperties,
            string propertyName,
            object propertyValue,
            EpmSourcePathSegment epmSourcePathSegment)
        {
            // check whether the property is projected; if no EPM is specified for the property the projection decides
            bool propertyProjected = !projectedProperties.ShouldSkipProperty(propertyName);

            bool useV1ProviderBehavior = this.MessageWriterSettings.WriterBehavior == null ? false : this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior;

            if (useV1ProviderBehavior && owningType != null && owningType.IsODataComplexTypeKind())
            {
                IEdmComplexType owningComplexType = (IEdmComplexType)owningType;
                CachedPrimitiveKeepInContentAnnotation keepInContentAnnotation = this.Model.EpmCachedKeepPrimitiveInContent(owningComplexType);
                if (keepInContentAnnotation != null && keepInContentAnnotation.IsKeptInContent(propertyName))
                {
                    return(propertyProjected);
                }
            }

            // We sometimes write properties into content even if asked not to.
            // If the property value is null and the property (or one of its descendant properties) is mapped,
            // we always write into content, even if the property was not projected.
            if (propertyValue == null && epmSourcePathSegment != null)
            {
                return(true);
            }

            EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment);

            if (entityPropertyMapping == null)
            {
                return(propertyProjected);
            }

            string stringPropertyValue = propertyValue as string;

            if (stringPropertyValue != null && stringPropertyValue.Length == 0)
            {
                // If the property value is an empty string and we should be writing it into an ATOM element which does not allow empty string
                // we write it into content as well, also even if the property was not projected.
                switch (entityPropertyMapping.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorEmail:
                case SyndicationItemProperty.ContributorUri:
                    return(true);

                default:
                    break;
                }
            }

            return(entityPropertyMapping.KeepInContent && propertyProjected);
        }
        /// <summary>
        /// Writes a stream property to the ATOM payload
        /// </summary>
        /// <param name="streamProperty">The stream property to create the payload for.</param>
        /// <param name="owningType">The <see cref="IEdmEntityType"/> instance for which the stream property defined on.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        internal void WriteStreamProperty(
            ODataProperty streamProperty,
            IEdmEntityType owningType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(streamProperty != null, "Stream property must not be null.");
            Debug.Assert(streamProperty.Value != null, "The media resource of the stream property must not be null.");

            WriterValidationUtils.ValidatePropertyNotNull(streamProperty);
            string propertyName = streamProperty.Name;

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

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(streamProperty);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(streamProperty.Name, owningType, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);

            WriterValidationUtils.ValidateStreamReferenceProperty(streamProperty, edmProperty, this.Version, this.WritingResponse);
            ODataStreamReferenceValue streamReferenceValue = (ODataStreamReferenceValue)streamProperty.Value;

            WriterValidationUtils.ValidateStreamReferenceValue(streamReferenceValue, false /*isDefaultStream*/);
            if (owningType != null && owningType.IsOpen && edmProperty == null)
            {
                ValidationUtils.ValidateOpenPropertyValue(streamProperty.Name, streamReferenceValue, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            }

            AtomStreamReferenceMetadata streamReferenceMetadata = streamReferenceValue.GetAnnotation <AtomStreamReferenceMetadata>();
            string contentType = streamReferenceValue.ContentType;
            string linkTitle   = streamProperty.Name;

            Uri readLink = streamReferenceValue.ReadLink;

            if (readLink != null)
            {
                string readLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, false);

                AtomLinkMetadata readLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.SelfLink;
                AtomLinkMetadata mergedMetadata   = ODataAtomWriterMetadataUtils.MergeLinkMetadata(readLinkMetadata, readLinkRelation, readLink, linkTitle, contentType);
                this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, null /* etag */);
            }

            Uri editLink = streamReferenceValue.EditLink;

            if (editLink != null)
            {
                string editLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, true);

                AtomLinkMetadata editLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.EditLink;
                AtomLinkMetadata mergedMetadata   = ODataAtomWriterMetadataUtils.MergeLinkMetadata(editLinkMetadata, editLinkRelation, editLink, linkTitle, contentType);
                this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, streamReferenceValue.ETag);
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Determines if the property with the specified value should be written into content or not.
        /// </summary>
        /// <param name="projectedProperties">The set of projected properties for the owning type</param>
        /// <param name="propertyName">The name of the property to be checked.</param>
        /// <returns>true if the property should be written into content, or false otherwise</returns>
        private static bool ShouldWritePropertyInContent(
            ProjectedPropertiesAnnotation projectedProperties,
            string propertyName)
        {
            // check whether the property is projected;
            bool propertyProjected = !projectedProperties.ShouldSkipProperty(propertyName);

            return(propertyProjected);
        }
Esempio n. 8
0
 protected override void EndEntry(ODataEntry entry)
 {
     if (entry != null)
     {
         ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>();
         this.jsonEntryAndFeedSerializer.WriteProperties(base.EntryEntityType, entry.Properties, false, base.DuplicatePropertyNamesChecker, projectedProperties);
         this.jsonOutputContext.JsonWriter.EndObjectScope();
     }
 }
Esempio n. 9
0
        /// <summary>
        /// Start writing an entry.
        /// </summary>
        /// <param name="entry">The entry to write.</param>
        protected override void StartEntry(ODataEntry entry)
        {
            ODataNavigationLink parentNavLink = this.ParentNavigationLink;

            if (parentNavLink != null)
            {
                // Write the property name of an expanded navigation property to start the value.
                this.jsonWriter.WriteName(parentNavLink.Name);
            }

            if (entry == null)
            {
                Debug.Assert(
                    parentNavLink != null && !parentNavLink.IsCollection.Value,
                    "when entry == null, it has to be and expanded single entry navigation");

                // this is a null expanded single entry and it is null, so write a JSON null as value.
                this.jsonWriter.WriteValue((string)null);
                return;
            }

            // Write just the object start, nothing else, since we might not have complete information yet
            this.jsonWriter.StartObjectScope();

            JsonLightEntryScope entryScope = this.CurrentEntryScope;

            if (this.IsTopLevel)
            {
                // Write odata.context
                this.jsonLightEntryAndFeedSerializer.WriteEntryContextUri(entryScope.GetOrCreateTypeContext(this.jsonLightOutputContext.Model, this.jsonLightOutputContext.WritingResponse));
            }

            // Write the metadata
            this.jsonLightEntryAndFeedSerializer.WriteEntryStartMetadataProperties(entryScope);
            this.jsonLightEntryAndFeedSerializer.WriteEntryMetadataProperties(entryScope);

            // Write custom instance annotations
            this.jsonLightEntryAndFeedSerializer.InstanceAnnotationWriter.WriteInstanceAnnotations(entry.InstanceAnnotations, entryScope.InstanceAnnotationWriteTracker);

            // Write the properties
            ProjectedPropertiesAnnotation projectedProperties = GetProjectedPropertiesAnnotation(entryScope);

            this.jsonLightEntryAndFeedSerializer.JsonLightValueSerializer.AssertRecursionDepthIsZero();
            this.jsonLightEntryAndFeedSerializer.WriteProperties(
                this.EntryEntityType,
                entry.Properties,
                false /* isComplexValue */,
                this.DuplicatePropertyNamesChecker,
                projectedProperties);
            this.jsonLightEntryAndFeedSerializer.JsonLightValueSerializer.AssertRecursionDepthIsZero();

            // COMPAT 48: Position of navigation properties/links in JSON differs.
        }
Esempio n. 10
0
 protected override void StartEntry(ODataEntry entry)
 {
     if (entry == null)
     {
         this.jsonOutputContext.JsonWriter.WriteValue((string)null);
     }
     else
     {
         this.jsonOutputContext.JsonWriter.StartObjectScope();
         ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>();
         this.jsonEntryAndFeedSerializer.WriteEntryMetadata(entry, projectedProperties, base.EntryEntityType, base.DuplicatePropertyNamesChecker);
     }
 }
Esempio n. 11
0
        /// <summary>
        /// Writes a property with a complex value in ATOM format.
        /// </summary>
        /// <param name="complexValue">The complex value to write.</param>
        /// <param name="propertyName">The name of the property being written.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforeValueAction">Action called before the complex value is written, if it's actually written.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="propertyTypeReference">The type information for the property being written.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property we're writing. (can be null)</param>
        /// <param name="complexValueProjectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        private bool WriteComplexValueProperty(
            ODataComplexValue complexValue,
            string propertyName,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforeValueAction,
            EpmValueCache epmValueCache,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation complexValueProjectedProperties)
        {
            // Complex properties are written recursively.
            DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();

            if (isTopLevel)
            {
                // Top-level property must always write the property element
                Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null");
                this.WritePropertyStart(beforeValueAction, propertyName, isWritingCollection, /*isTopLevel*/ true);
                this.AssertRecursionDepthIsZero();
                this.WriteComplexValue(
                    complexValue,
                    propertyTypeReference,
                    isOpenPropertyType,
                    isWritingCollection,
                    null /* beforeValueAction */,
                    null /* afterValueAction */,
                    complexValuePropertyNamesChecker,
                    null /* collectionValidator */,
                    epmValueCache,
                    epmSourcePathSegment,
                    null /* projectedProperties */);
                this.AssertRecursionDepthIsZero();
                this.WritePropertyEnd();
                return(true);
            }

            return(this.WriteComplexValue(
                       complexValue,
                       propertyTypeReference,
                       isOpenPropertyType,
                       isWritingCollection,
                       () => this.WritePropertyStart(beforeValueAction, propertyName, isWritingCollection, /*isTopLevel*/ false),
                       this.WritePropertyEnd,
                       complexValuePropertyNamesChecker,
                       null /* collectionValidator */,
                       epmValueCache,
                       epmSourcePathSegment,
                       complexValueProjectedProperties));
        }
Esempio n. 12
0
        /// <summary>
        /// Determines if a property should be written or skipped.
        /// </summary>
        /// <param name="projectedProperties">The projected properties annotation to use (can be null).</param>
        /// <param name="propertyName">The name of the property to check.</param>
        /// <returns>true if the property should be skipped, false to write the property.</returns>
        internal static bool ShouldSkipProperty(this ProjectedPropertiesAnnotation projectedProperties, string propertyName)
        {
            DebugUtils.CheckNoExternalCallers();

            if (projectedProperties == null)
            {
                return(false);
            }
            else if (object.ReferenceEquals(ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance, projectedProperties))
            {
                return(true);
            }
            else if (object.ReferenceEquals(ProjectedPropertiesAnnotation.AllProjectedPropertiesInstance, projectedProperties))
            {
                return(false);
            }

            return(!projectedProperties.IsPropertyProjected(propertyName));
        }
Esempio n. 13
0
        /// <summary>
        /// Write the given collection of properties.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="cachedProperties">Collection of cached properties for the entry.</param>
        /// <param name="isWritingCollection">true if we are writing a top level collection instead of an entry.</param>
        /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param>
        /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmSourcePathSegment">The EPM source path segment which points to the property which sub-properites we're writing. (can be null)</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        internal bool WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable <ODataProperty> cachedProperties,
            bool isWritingCollection,
            Action beforePropertiesAction,
            Action afterPropertiesAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            if (cachedProperties == null)
            {
                return(false);
            }

            bool propertyWritten = false;

            foreach (ODataProperty property in cachedProperties)
            {
                propertyWritten |= this.WriteProperty(
                    property,
                    owningType,
                    /*isTopLevel*/ false,
                    isWritingCollection,
                    propertyWritten ? null : beforePropertiesAction,
                    epmValueCache,
                    epmSourcePathSegment,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }

            if (afterPropertiesAction != null && propertyWritten)
            {
                afterPropertiesAction();
            }

            return(propertyWritten);
        }
Esempio n. 14
0
        /// <summary>
        /// Start writing an entry.
        /// </summary>
        /// <param name="entry">The entry to write.</param>
        protected override void StartEntry(ODataEntry entry)
        {
            if (entry == null)
            {
                Debug.Assert(
                    this.ParentNavigationLink != null && !this.ParentNavigationLink.IsCollection.Value,
                    "when entry == null, it has to be and expanded single entry navigation");

                // this is a null expanded single entry and it is null, so write a JSON null as value.
                this.verboseJsonOutputContext.JsonWriter.WriteValue(null);
                return;
            }

            // Write just the object start, nothing else, since we might not have complete information yet
            this.verboseJsonOutputContext.JsonWriter.StartObjectScope();

            // Get the projected properties
            ProjectedPropertiesAnnotation projectedProperties = GetProjectedPropertiesAnnotation(this.CurrentScope);

            // Write the metadata
            this.verboseJsonEntryAndFeedSerializer.WriteEntryMetadata(entry, projectedProperties, this.EntryEntityType, this.DuplicatePropertyNamesChecker);
        }
Esempio n. 15
0
        private bool ShouldWritePropertyInContent(IEdmStructuredType owningType, ProjectedPropertiesAnnotation projectedProperties, string propertyName, object propertyValue, EpmSourcePathSegment epmSourcePathSegment)
        {
            bool flag = !projectedProperties.ShouldSkipProperty(propertyName);

            if ((((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && (owningType != null)) && owningType.IsODataComplexTypeKind())
            {
                IEdmComplexType complexType = (IEdmComplexType)owningType;
                CachedPrimitiveKeepInContentAnnotation annotation = base.Model.EpmCachedKeepPrimitiveInContent(complexType);
                if ((annotation != null) && annotation.IsKeptInContent(propertyName))
                {
                    return(flag);
                }
            }
            if ((propertyValue == null) && (epmSourcePathSegment != null))
            {
                return(true);
            }
            EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment);

            if (entityPropertyMapping == null)
            {
                return(flag);
            }
            string str = propertyValue as string;

            if ((str != null) && (str.Length == 0))
            {
                switch (entityPropertyMapping.TargetSyndicationItem)
                {
                case SyndicationItemProperty.AuthorEmail:
                case SyndicationItemProperty.AuthorUri:
                case SyndicationItemProperty.ContributorEmail:
                case SyndicationItemProperty.ContributorUri:
                    return(true);
                }
            }
            return(entityPropertyMapping.KeepInContent && flag);
        }
Esempio n. 16
0
        protected override void EndEntry(ODataEntry entry)
        {
            Debug.Assert(
                this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.Value,
                "We should have already verified that the IsCollection matches the actual content of the link (feed/entry).");

            if (entry == null)
            {
                Debug.Assert(this.ParentNavigationLink != null, "When entry == null, it has to be an expanded single entry navigation.");

                // this is a null expanded single entry and it is null, an empty <m:inline /> will be written.
                this.CheckAndWriteParentNavigationLinkEndForInlineElement();
                return;
            }

            IEdmEntityType entryType = this.EntryEntityType;

            // Initialize the property value cache and cache the entry properties.
            EntryPropertiesValueCache propertyValueCache = new EntryPropertiesValueCache(entry);

            // Get the projected properties annotation
            AtomEntryScope currentEntryScope = this.CurrentEntryScope;
            ProjectedPropertiesAnnotation projectedProperties = GetProjectedPropertiesAnnotation(currentEntryScope);
            AtomEntryMetadata             entryMetadata       = entry.Atom();

            if (!currentEntryScope.IsElementWritten(AtomElement.Id))
            {
                // NOTE: We write even null id, in that case we generate an empty atom:id element.
                bool isTransient = entry.IsTransient;
                this.atomEntryAndFeedSerializer.WriteEntryId(entry.Id, isTransient);
            }

            Uri editLink = entry.EditLink;

            if (editLink != null && !currentEntryScope.IsElementWritten(AtomElement.EditLink))
            {
                this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata);
            }

            Uri readLink = entry.ReadLink;

            if (readLink != null && readLink != editLink && !currentEntryScope.IsElementWritten(AtomElement.ReadLink))
            {
                this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata);
            }

            // write entry metadata
            this.atomEntryAndFeedSerializer.WriteEntryMetadata(entryMetadata, this.updatedTime);

            // stream properties
            IEnumerable <ODataProperty> streamProperties = propertyValueCache.EntryStreamProperties;

            if (streamProperties != null)
            {
                foreach (ODataProperty streamProperty in streamProperties)
                {
                    this.atomEntryAndFeedSerializer.WriteStreamProperty(
                        streamProperty,
                        entryType,
                        this.DuplicatePropertyNamesChecker,
                        projectedProperties);
                }
            }

            // actions
            IEnumerable <ODataAction> actions = entry.Actions;

            if (actions != null)
            {
                foreach (ODataAction action in actions)
                {
                    ValidationUtils.ValidateOperationNotNull(action, true);
                    this.atomEntryAndFeedSerializer.WriteOperation(action);
                }
            }

            // functions
            IEnumerable <ODataFunction> functions = entry.Functions;

            if (functions != null)
            {
                foreach (ODataFunction function in functions)
                {
                    ValidationUtils.ValidateOperationNotNull(function, false);
                    this.atomEntryAndFeedSerializer.WriteOperation(function);
                }
            }

            // write the content
            this.WriteEntryContent(
                entry,
                entryType,
                propertyValueCache,
                projectedProperties);

            this.WriteInstanceAnnotations(entry.InstanceAnnotations, currentEntryScope.InstanceAnnotationWriteTracker);

            // </entry>
            this.atomOutputContext.XmlWriter.WriteEndElement();

            this.CheckAndWriteParentNavigationLinkEndForInlineElement();
        }
        /// <summary>
        /// Write the metadata for an OData association link; makes sure any duplicate of the link's values duplicated in metadata are equal.
        /// </summary>
        /// <param name="associationLink">The association link for which to write the metadata.</param>
        /// <param name="owningType">The <see cref="IEdmEntityType"/> instance the association link is defined on.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        internal void WriteAssociationLink(
            ODataAssociationLink associationLink,
            IEdmEntityType owningType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            ValidationUtils.ValidateAssociationLinkNotNull(associationLink);
            if (projectedProperties.ShouldSkipProperty(associationLink.Name))
            {
                return;
            }

            this.ValidateAssociationLink(associationLink, owningType);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(associationLink);

            AtomLinkMetadata linkMetadata = associationLink.GetAnnotation<AtomLinkMetadata>();
            string linkRelation = AtomUtils.ComputeODataAssociationLinkRelation(associationLink);
            AtomLinkMetadata mergedLinkMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(linkMetadata, linkRelation, associationLink.Url, associationLink.Name, MimeConstants.MimeApplicationXml);
            this.atomEntryMetadataSerializer.WriteAtomLink(mergedLinkMetadata, null /* etag*/);
        }
        private IEnumerable <ODataItem> CreatePayload(ProjectedPropertiesTestCase testCase)
        {
            // First create the entry itself (it might get wrapped later)
            ODataEntry entry = new ODataEntry()
            {
                TypeName   = "TestModel.EntityType",
                Properties = new List <ODataProperty>()
                {
                    new ODataProperty {
                        Name = "StringProperty", Value = "foo"
                    },
                    new ODataProperty {
                        Name = "NumberProperty", Value = 42
                    },
                    new ODataProperty {
                        Name = "SimpleComplexProperty", Value = new ODataComplexValue
                        {
                            TypeName   = "TestModel.SimplexComplexType",
                            Properties = new ODataProperty[] {
                                new ODataProperty {
                                    Name = "Name", Value = "Bart"
                                }
                            }
                        }
                    },
                    new ODataProperty {
                        Name = "DeepComplexProperty", Value = new ODataComplexValue
                        {
                            TypeName   = "TestModel.NestedComplexType",
                            Properties = new ODataProperty[] {
                                new ODataProperty {
                                    Name = "InnerComplexProperty", Value = new ODataComplexValue
                                    {
                                        TypeName   = "TestModel.SimplexComplexType2",
                                        Properties = new ODataProperty[] {
                                            new ODataProperty {
                                                Name = "Value", Value = 43
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },

                    new ODataProperty {
                        Name = "PrimitiveCollection", Value = new ODataCollectionValue
                        {
                            TypeName = "Collection(Edm.String)",
                            Items    = new object[] { "Simpson" }
                        }
                    },
                    new ODataProperty {
                        Name = "ComplexCollection", Value = new ODataCollectionValue
                        {
                            TypeName = "Collection(TestModel.RatingComplexType)",
                            Items    = new object[] {
                                new ODataComplexValue
                                {
                                    TypeName   = "TestModel.RatingComplexType",
                                    Properties = new ODataProperty[] { new ODataProperty {
                                                                           Name = "Rating", Value = -3
                                                                       } }
                                }
                            }
                        }
                    }
                },
                SerializationInfo = MySerializationInfo
            };

            if (testCase.ResponseOnly)
            {
                // Add a stream property for responses
                ((List <ODataProperty>)entry.Properties).Add(new ODataProperty {
                    Name = "NamedStream", Value = new ODataStreamReferenceValue {
                        EditLink = new Uri("http://odata.org/namedstream")
                    }
                });
            }

            ODataItem[] entryItems = new ODataItem[]
            {
                entry,
                new ODataNavigationLink {
                    Name = "DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred"), AssociationLinkUrl = testCase.ResponseOnly ? new Uri("http://odata.org/associationlink2") : null
                },
                null, // End deferred link

                new ODataNavigationLink {
                    Name = "ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/entry")
                },
                new ODataEntry()
                {
                    TypeName   = "TestModel.ExpandedEntryType",
                    Properties = new ODataProperty[] {
                        new ODataProperty {
                            Name = "ExpandedEntryName", Value = "bar"
                        }
                    },
                    SerializationInfo = MySerializationInfo
                },
                new ODataNavigationLink {
                    Name = "ExpandedEntry_DeferredNavigation", IsCollection = false, Url = new Uri("http://odata.org/deferred")
                },
                null,         // End deffered link
                new ODataNavigationLink {
                    Name = "ExpandedEntry_ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed")
                },
                new ODataFeed {
                    Id = new Uri("http://test/feedid1"), SerializationInfo = MySerializationInfo
                },
                null, // End feed
                null, // End exanded expanded feed link
                null, // End expanded entry
                null, // End expanded entry nav link

                new ODataNavigationLink {
                    Name = "ExpandedFeed", IsCollection = true, Url = new Uri("http://odata.org/feed")
                },
                new ODataFeed {
                    Id = new Uri("http://test/feedid2")
                },
                new ODataEntry {
                    TypeName = "TestModel.EntityType"
                },
                null,         // End entry
                new ODataEntry {
                    TypeName = "TestModel.EntityType", SerializationInfo = MySerializationInfo
                },
                null, // End entry
                null, // End expanded feed
                null, // End expanded feed nav link

                null, // End the top-level entry
            };

            ProjectedPropertiesAnnotation projectedProperties = testCase.TopLevelProjectedProperties;

            if (!testCase.NestedPayload)
            {
                this.Assert.IsNull(testCase.NestedProjectedProperties, "For a non-nested payload, no nested annotation must be specified.");
                entry.SetAnnotation(projectedProperties);
                return(entryItems);
            }

            // If we are processing a test case for a nested payload, wrap the entry items into a wrapping entry with an expanded navigation link.
            ODataEntry wrappingEntry = new ODataEntry()
            {
                TypeName   = "TestModel.WrappingEntityType",
                Properties = new[] { new ODataProperty {
                                         Name = "Wrapping_ID", Value = 1
                                     } },
                SerializationInfo = MySerializationInfo
            };
            IEnumerable <ODataItem> wrappedItems =
                new ODataItem[] { wrappingEntry, new ODataNavigationLink {
                                      Name = "Wrapping_ExpandedEntry", IsCollection = false, Url = new Uri("http://odata.org/wrapping")
                                  } }
            .Concat(entryItems)
            .Concat(new ODataItem[] { null, null });

            ProjectedPropertiesAnnotation nestedProjectedProperties = testCase.NestedProjectedProperties;

            entry.SetAnnotation(nestedProjectedProperties);
            wrappingEntry.SetAnnotation(projectedProperties);

            return(wrappedItems);
        }
Esempio n. 19
0
        protected override void EndEntry(ODataEntry entry)
        {
            Debug.Assert(
                this.ParentNavigationLink == null || !this.ParentNavigationLink.IsCollection.Value,
                "We should have already verified that the IsCollection matches the actual content of the link (feed/entry).");

            if (entry == null)
            {
                Debug.Assert(this.ParentNavigationLink != null, "When entry == null, it has to be an expanded single entry navigation.");

                // this is a null expanded single entry and it is null, an empty <m:inline /> will be written.
                this.CheckAndWriteParentNavigationLinkEndForInlineElement();
                return;
            }

            IEdmEntityType entryType = this.EntryEntityType;

            // Initialize the property value cache and cache the entry properties.
            EntryPropertiesValueCache propertyValueCache = new EntryPropertiesValueCache(entry);

            // NOTE: when writing, we assume the model has been validated already and thus pass int.MaxValue for the maxMappingCount.
            ODataEntityPropertyMappingCache epmCache = this.atomOutputContext.Model.EnsureEpmCache(entryType, /*maxMappingCount*/ int.MaxValue);

            // Populate the property value cache based on the EPM source tree information.
            // We do this since we need to write custom EPM after the properties below and don't
            // want to cache all properties just for the case that they are used in a custom EPM.
            if (epmCache != null)
            {
                EpmWriterUtils.CacheEpmProperties(propertyValueCache, epmCache.EpmSourceTree);
            }

            // Get the projected properties annotation
            ProjectedPropertiesAnnotation projectedProperties = entry.GetAnnotation <ProjectedPropertiesAnnotation>();

            AtomEntryScope    currentEntryScope = this.CurrentEntryScope;
            AtomEntryMetadata entryMetadata     = entry.Atom();

            if (!currentEntryScope.IsElementWritten(AtomElement.Id))
            {
                // NOTE: We write even null id, in that case we generate an empty atom:id element.
                this.atomEntryAndFeedSerializer.WriteEntryId(entry.Id);
            }

            Uri editLink = entry.EditLink;

            if (editLink != null && !currentEntryScope.IsElementWritten(AtomElement.EditLink))
            {
                this.atomEntryAndFeedSerializer.WriteEntryEditLink(editLink, entryMetadata);
            }

            Uri readLink = entry.ReadLink;

            if (readLink != null && !currentEntryScope.IsElementWritten(AtomElement.ReadLink))
            {
                this.atomEntryAndFeedSerializer.WriteEntryReadLink(readLink, entryMetadata);
            }

            // write entry metadata including syndication EPM
            AtomEntryMetadata epmEntryMetadata = null;

            if (epmCache != null)
            {
                ODataVersionChecker.CheckEntityPropertyMapping(this.atomOutputContext.Version, entryType, this.atomOutputContext.Model);

                epmEntryMetadata = EpmSyndicationWriter.WriteEntryEpm(
                    epmCache.EpmTargetTree,
                    propertyValueCache,
                    entryType.ToTypeReference().AsEntity(),
                    this.atomOutputContext);
            }

            this.atomEntryAndFeedSerializer.WriteEntryMetadata(entryMetadata, epmEntryMetadata, this.updatedTime);

            // stream properties
            IEnumerable <ODataProperty> streamProperties = propertyValueCache.EntryStreamProperties;

            if (streamProperties != null)
            {
                foreach (ODataProperty streamProperty in streamProperties)
                {
                    this.atomEntryAndFeedSerializer.WriteStreamProperty(
                        streamProperty,
                        entryType,
                        this.DuplicatePropertyNamesChecker,
                        projectedProperties);
                }
            }

            // association links
            IEnumerable <ODataAssociationLink> associationLinks = entry.AssociationLinks;

            if (associationLinks != null)
            {
                foreach (ODataAssociationLink associationLink in associationLinks)
                {
                    this.atomEntryAndFeedSerializer.WriteAssociationLink(
                        associationLink,
                        entryType,
                        this.DuplicatePropertyNamesChecker,
                        projectedProperties);
                }
            }

            // actions
            IEnumerable <ODataAction> actions = entry.Actions;

            if (actions != null)
            {
                foreach (ODataAction action in actions)
                {
                    ValidationUtils.ValidateOperationNotNull(action, true);
                    this.atomEntryAndFeedSerializer.WriteOperation(action);
                }
            }

            // functions
            IEnumerable <ODataFunction> functions = entry.Functions;

            if (functions != null)
            {
                foreach (ODataFunction function in functions)
                {
                    ValidationUtils.ValidateOperationNotNull(function, false);
                    this.atomEntryAndFeedSerializer.WriteOperation(function);
                }
            }

            // write the content
            this.WriteEntryContent(
                entry,
                entryType,
                propertyValueCache,
                epmCache == null ? null : epmCache.EpmSourceTree.Root,
                projectedProperties);

            // write custom EPM
            if (epmCache != null)
            {
                EpmCustomWriter.WriteEntryEpm(
                    this.atomOutputContext.XmlWriter,
                    epmCache.EpmTargetTree,
                    propertyValueCache,
                    entryType.ToTypeReference().AsEntity(),
                    this.atomOutputContext);
            }

            // </entry>
            this.atomOutputContext.XmlWriter.WriteEndElement();

            this.EndEntryXmlCustomization(entry);

            this.CheckAndWriteParentNavigationLinkEndForInlineElement();
        }
Esempio n. 20
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. 21
0
        /// <summary>
        /// Write the content of the given entry.
        /// </summary>
        /// <param name="entry">The entry for which to write properties.</param>
        /// <param name="entryType">The <see cref="IEdmEntityType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="propertiesValueCache">The cache of properties.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        private void WriteEntryContent(
            ODataEntry entry,
            IEdmEntityType entryType, 
            EntryPropertiesValueCache propertiesValueCache, 
            ProjectedPropertiesAnnotation projectedProperties)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(propertiesValueCache != null, "propertiesValueCache != null");

            ODataStreamReferenceValue mediaResource = entry.MediaResource;
            if (mediaResource == null)
            {
                // <content type="application/xml">
                this.atomOutputContext.XmlWriter.WriteStartElement(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomContentElementName,
                    AtomConstants.AtomNamespace);

                this.atomOutputContext.XmlWriter.WriteAttributeString(
                    AtomConstants.AtomTypeAttributeName,
                    MimeConstants.MimeApplicationXml);

                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
                this.atomEntryAndFeedSerializer.WriteProperties(
                    entryType,
                    propertiesValueCache.EntryProperties,
                    false /* isWritingCollection */,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd,
                    this.DuplicatePropertyNamesChecker,
                    projectedProperties);
                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();

                // </content>
                this.atomOutputContext.XmlWriter.WriteEndElement();
            }
            else
            {
                WriterValidationUtils.ValidateStreamReferenceValue(mediaResource, true);

                this.atomEntryAndFeedSerializer.WriteEntryMediaEditLink(mediaResource);

                if (mediaResource.ReadLink != null)
                {
                    // <content type="type" src="src">
                    this.atomOutputContext.XmlWriter.WriteStartElement(
                        AtomConstants.AtomNamespacePrefix,
                        AtomConstants.AtomContentElementName,
                        AtomConstants.AtomNamespace);

                    Debug.Assert(!string.IsNullOrEmpty(mediaResource.ContentType), "The default stream content type should have been validated by now. If we have a read link we must have a non-empty content type as well.");
                    this.atomOutputContext.XmlWriter.WriteAttributeString(
                        AtomConstants.AtomTypeAttributeName,
                        mediaResource.ContentType);

                    this.atomOutputContext.XmlWriter.WriteAttributeString(
                        AtomConstants.MediaLinkEntryContentSourceAttributeName,
                        this.atomEntryAndFeedSerializer.UriToUrlAttributeValue(mediaResource.ReadLink));

                    // </content>
                    this.atomOutputContext.XmlWriter.WriteEndElement();
                }

                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
                this.atomEntryAndFeedSerializer.WriteProperties(
                    entryType,
                    propertiesValueCache.EntryProperties, 
                    false /* isWritingCollection */,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd,
                    this.DuplicatePropertyNamesChecker,
                    projectedProperties);
                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
            }
        }
Esempio n. 22
0
        /// <summary>
        /// Determines if a property should be written or skipped.
        /// </summary>
        /// <param name="projectedProperties">The projected properties annotation to use (can be null).</param>
        /// <param name="propertyName">The name of the property to check.</param>
        /// <returns>true if the property should be skipped, false to write the property.</returns>
        internal static bool ShouldSkipProperty(this ProjectedPropertiesAnnotation projectedProperties, string propertyName)
        {
            DebugUtils.CheckNoExternalCallers();

            return(projectedProperties != null && !projectedProperties.IsPropertyProjected(propertyName));
        }
Esempio n. 23
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. 24
0
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The owning type for the <paramref name="property"/> or null if no metadata is available.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
        /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param>
        /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if the property was actually written, false otherwise.</returns>
        private bool WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforePropertyAction,
            EpmValueCache epmValueCache,
            EpmSourcePathSegment epmParentSourcePathSegment,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);

            object value        = property.Value;
            string propertyName = property.Name;
            EpmSourcePathSegment epmSourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);

            //// TODO: If we implement type conversions the value needs to be converted here
            ////       since the next method call needs to know if the value is a string or not in some cases.

            ODataComplexValue             complexValue = value as ODataComplexValue;
            ProjectedPropertiesAnnotation complexValueProjectedProperties = null;

            if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, value, epmSourcePathSegment))
            {
                // If ShouldWritePropertyInContent returns false for a comlex value we have to continue
                // writing the property but set the projectedProperties to an empty array. The reason for this
                // is that we might find EPM on a nested property that has a null value and thus must be written
                // in content (in which case the parent property also has to be written).
                // This only applies if we have EPM information for the property.
                if (epmSourcePathSegment != null && complexValue != null)
                {
                    Debug.Assert(!projectedProperties.IsPropertyProjected(propertyName), "ShouldWritePropertyInContent must not return false for a projected complex property.");
                    complexValueProjectedProperties = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance;
                }
                else
                {
                    return(false);
                }
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty      edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            if (value is ODataStreamReferenceValue)
            {
                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }

            // 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.
            if ((propertyTypeReference != null && propertyTypeReference.IsSpatial()) ||
                (propertyTypeReference == null && value is System.Spatial.ISpatial))
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            // Null property value.
            if (value == null)
            {
                this.WriteNullPropertyValue(propertyTypeReference, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return(true);
            }

            bool isOpenPropertyType = owningType != null && owningType.IsOpen && propertyTypeReference == null;

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

            if (complexValue != null)
            {
                return(this.WriteComplexValueProperty(
                           complexValue,
                           propertyName,
                           isTopLevel,
                           isWritingCollection,
                           beforePropertyAction,
                           epmValueCache,
                           propertyTypeReference,
                           isOpenPropertyType,
                           epmSourcePathSegment,
                           complexValueProjectedProperties));
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                this.WriteCollectionValueProperty(
                    collectionValue,
                    propertyName,
                    isTopLevel,
                    isWritingCollection,
                    beforePropertyAction,
                    propertyTypeReference,
                    isOpenPropertyType);

                return(true);
            }

            // If the value isn't one of the value types tested for already, it must be a non-null primitive.
            this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = property.ODataValue.GetAnnotation <SerializationTypeNameAnnotation>();

            this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation);
            this.WritePropertyEnd();
            return(true);
        }
        /// <summary>
        /// Write the given collection of properties.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="cachedProperties">Collection of cached properties for the entry.</param>
        /// <param name="isWritingCollection">true if we are writing a top level collection instead of an entry.</param>
        /// <param name="beforePropertiesAction">Action which is called before the properties are written, if there are any property.</param>
        /// <param name="afterPropertiesAction">Action which is called after the properties are written, if there are any property.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        internal bool WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable<ODataProperty> cachedProperties,
            bool isWritingCollection,
            Action beforePropertiesAction,
            Action afterPropertiesAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            if (cachedProperties == null)
            {
                return false;
            }

            bool propertyWritten = false;
            foreach (ODataProperty property in cachedProperties)
            {
                propertyWritten |= this.WriteProperty(
                    property,
                    owningType,
                    /*isTopLevel*/false,
                    isWritingCollection,
                    propertyWritten ? null : beforePropertiesAction,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }

            if (afterPropertiesAction != null && propertyWritten)
            {
                afterPropertiesAction();
            }

            return propertyWritten;
        }
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The owning type for the <paramref name="property"/> or null if no metadata is available.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if the property was actually written, false otherwise.</returns>
        private bool WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforePropertyAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            WriterValidationUtils.ValidatePropertyNotNull(property);

            object value = property.Value;
            string propertyName = property.Name;
            //// TODO: If we implement type conversions the value needs to be converted here
            ////       since the next method call needs to know if the value is a string or not in some cases.

            ODataComplexValue complexValue = value as ODataComplexValue;
            ProjectedPropertiesAnnotation complexValueProjectedProperties = null;
            if (!ShouldWritePropertyInContent(projectedProperties, propertyName))
            {
                return false;
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            if (value is ODataStreamReferenceValue)
            {
                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }

            // Null property value.
            if (value == null)
            {
                this.WriteNullPropertyValue(propertyTypeReference, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return true;
            }

            bool isOpenPropertyType = owningType != null && owningType.IsOpen && propertyTypeReference == null;
            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value);
            }

            if (complexValue != null)
            {
                return this.WriteComplexValueProperty(
                    complexValue,
                    propertyName,
                    isTopLevel,
                    isWritingCollection,
                    beforePropertyAction,
                    propertyTypeReference,
                    isOpenPropertyType,
                    complexValueProjectedProperties);
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;
            if (collectionValue != null)
            {
                this.WriteCollectionValueProperty(
                    collectionValue,
                    propertyName,
                    isTopLevel,
                    isWritingCollection,
                    beforePropertyAction,
                    propertyTypeReference,
                    isOpenPropertyType);

                return true;
            }

            // If the value isn't one of the value types tested for already, it must be a non-null primitive or enum type.
            this.WritePropertyStart(beforePropertyAction, property, isWritingCollection, isTopLevel);
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = property.ODataValue.GetAnnotation<SerializationTypeNameAnnotation>();
            ODataEnumValue enumValue = value as ODataEnumValue;
            if (enumValue != null)
            {
                this.WriteEnumValue(enumValue, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation);
            }
            else
            {
                this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation);
            }

            this.WritePropertyEnd();
            return true;
        }
 /// <summary>
 /// Determines if the property with the specified value should be written into content or not.
 /// </summary>
 /// <param name="projectedProperties">The set of projected properties for the owning type</param>
 /// <param name="propertyName">The name of the property to be checked.</param>
 /// <returns>true if the property should be written into content, or false otherwise</returns>
 private static bool ShouldWritePropertyInContent(
     ProjectedPropertiesAnnotation projectedProperties,
     string propertyName)
 {
     // check whether the property is projected;
     bool propertyProjected = !projectedProperties.ShouldSkipProperty(propertyName);
     return propertyProjected;
 }
Esempio n. 28
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. 29
0
        private bool WriteProperty(ODataProperty property, IEdmStructuredType owningType, bool isTopLevel, bool isWritingCollection, Action beforePropertyAction, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
        {
            Action beforeValueAction = null;
            Action afterValueAction  = null;

            WriterValidationUtils.ValidatePropertyNotNull(property);
            object propertyValue = property.Value;
            string propertyName  = property.Name;
            EpmSourcePathSegment          propertySourcePathSegment = EpmWriterUtils.GetPropertySourcePathSegment(epmParentSourcePathSegment, propertyName);
            ODataComplexValue             complexValue = propertyValue as ODataComplexValue;
            ProjectedPropertiesAnnotation emptyProjectedPropertiesMarker = null;

            if (!this.ShouldWritePropertyInContent(owningType, projectedProperties, propertyName, propertyValue, propertySourcePathSegment))
            {
                if ((propertySourcePathSegment == null) || (complexValue == null))
                {
                    return(false);
                }
                emptyProjectedPropertiesMarker = ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker;
            }
            WriterValidationUtils.ValidateProperty(property);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);

            if (propertyValue is ODataStreamReferenceValue)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }
            if (((edmProperty != null) && edmProperty.Type.IsSpatial()) || ((edmProperty == null) && (propertyValue is ISpatial)))
            {
                ODataVersionChecker.CheckSpatialValue(base.Version);
            }
            if (propertyValue == null)
            {
                this.WriteNullPropertyValue(edmProperty, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return(true);
            }
            bool isOpenPropertyType = ((owningType != null) && owningType.IsOpen) && (edmProperty == null);

            if (isOpenPropertyType)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, propertyValue);
            }
            IEdmTypeReference metadataTypeReference = (edmProperty == null) ? null : edmProperty.Type;

            if (complexValue != null)
            {
                DuplicatePropertyNamesChecker checker = base.CreateDuplicatePropertyNamesChecker();
                if (isTopLevel)
                {
                    this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                    this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, null, null, checker, null, epmValueCache, propertySourcePathSegment, null);
                    this.WritePropertyEnd();
                    return(true);
                }
                if (beforeValueAction == null)
                {
                    beforeValueAction = delegate {
                        this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                    };
                }
                if (afterValueAction == null)
                {
                    afterValueAction = delegate {
                        this.WritePropertyEnd();
                    };
                }
                return(this.WriteComplexValue(complexValue, metadataTypeReference, isOpenPropertyType, isWritingCollection, beforeValueAction, afterValueAction, checker, null, epmValueCache, propertySourcePathSegment, emptyProjectedPropertiesMarker));
            }
            ODataCollectionValue collectionValue = propertyValue as ODataCollectionValue;

            if (collectionValue != null)
            {
                ODataVersionChecker.CheckCollectionValueProperties(base.Version, propertyName);
                this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
                this.WriteCollectionValue(collectionValue, metadataTypeReference, isOpenPropertyType, isWritingCollection);
                this.WritePropertyEnd();
                return(true);
            }
            this.WritePropertyStart(beforePropertyAction, propertyName, isWritingCollection, isTopLevel);
            this.WritePrimitiveValue(propertyValue, null, metadataTypeReference);
            this.WritePropertyEnd();
            return(true);
        }
Esempio n. 30
0
        /// <summary>
        /// Write the content of the given entry.
        /// </summary>
        /// <param name="entry">The entry for which to write properties.</param>
        /// <param name="entryType">The <see cref="IEdmEntityType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="propertiesValueCache">The cache of properties.</param>
        /// <param name="rootSourcePathSegment">The root of the EPM source tree, if there's an EPM applied.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        private void WriteEntryContent(
            ODataEntry entry,
            IEdmEntityType entryType,
            EntryPropertiesValueCache propertiesValueCache,
            EpmSourcePathSegment rootSourcePathSegment,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(propertiesValueCache != null, "propertiesValueCache != null");

            ODataStreamReferenceValue mediaResource = entry.MediaResource;

            if (mediaResource == null)
            {
                // <content type="application/xml">
                this.atomOutputContext.XmlWriter.WriteStartElement(
                    AtomConstants.AtomNamespacePrefix,
                    AtomConstants.AtomContentElementName,
                    AtomConstants.AtomNamespace);

                this.atomOutputContext.XmlWriter.WriteAttributeString(
                    AtomConstants.AtomTypeAttributeName,
                    MimeConstants.MimeApplicationXml);

                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
                this.atomEntryAndFeedSerializer.WriteProperties(
                    entryType,
                    propertiesValueCache.EntryProperties,
                    false /* isWritingCollection */,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd,
                    this.DuplicatePropertyNamesChecker,
                    propertiesValueCache,
                    rootSourcePathSegment,
                    projectedProperties);
                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();

                // </content>
                this.atomOutputContext.XmlWriter.WriteEndElement();
            }
            else
            {
                WriterValidationUtils.ValidateStreamReferenceValue(mediaResource, true);

                this.atomEntryAndFeedSerializer.WriteEntryMediaEditLink(mediaResource);

                if (mediaResource.ReadLink != null)
                {
                    // <content type="type" src="src">
                    this.atomOutputContext.XmlWriter.WriteStartElement(
                        AtomConstants.AtomNamespacePrefix,
                        AtomConstants.AtomContentElementName,
                        AtomConstants.AtomNamespace);

                    Debug.Assert(!string.IsNullOrEmpty(mediaResource.ContentType), "The default stream content type should have been validated by now. If we have a read link we must have a non-empty content type as well.");
                    this.atomOutputContext.XmlWriter.WriteAttributeString(
                        AtomConstants.AtomTypeAttributeName,
                        mediaResource.ContentType);

                    this.atomOutputContext.XmlWriter.WriteAttributeString(
                        AtomConstants.MediaLinkEntryContentSourceAttributeName,
                        this.atomEntryAndFeedSerializer.UriToUrlAttributeValue(mediaResource.ReadLink));

                    // </content>
                    this.atomOutputContext.XmlWriter.WriteEndElement();
                }

                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
                this.atomEntryAndFeedSerializer.WriteProperties(
                    entryType,
                    propertiesValueCache.EntryProperties,
                    false /* isWritingCollection */,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesStart,
                    this.atomEntryAndFeedSerializer.WriteEntryPropertiesEnd,
                    this.DuplicatePropertyNamesChecker,
                    propertiesValueCache,
                    rootSourcePathSegment,
                    projectedProperties);
                this.atomEntryAndFeedSerializer.AssertRecursionDepthIsZero();
            }
        }
Esempio n. 31
0
        /// <summary>
        /// Writes a single property in ATOM format.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The owning type for the <paramref name="property"/> or null if no metadata is available.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if the property was actually written, false otherwise.</returns>
        private bool WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforePropertyAction,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            WriterValidationUtils.ValidatePropertyNotNull(property);

            object value        = property.Value;
            string propertyName = property.Name;
            //// TODO: If we implement type conversions the value needs to be converted here
            ////       since the next method call needs to know if the value is a string or not in some cases.

            ODataComplexValue             complexValue = value as ODataComplexValue;
            ProjectedPropertiesAnnotation complexValueProjectedProperties = null;

            if (!ShouldWritePropertyInContent(projectedProperties, propertyName))
            {
                return(false);
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty      edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            if (value is ODataStreamReferenceValue)
            {
                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
            }

            // Null property value.
            if (value == null)
            {
                this.WriteNullPropertyValue(propertyTypeReference, propertyName, isTopLevel, isWritingCollection, beforePropertyAction);
                return(true);
            }

            bool isOpenPropertyType = owningType != null && owningType.IsOpen && propertyTypeReference == null;

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

            if (complexValue != null)
            {
                return(this.WriteComplexValueProperty(
                           complexValue,
                           propertyName,
                           isTopLevel,
                           isWritingCollection,
                           beforePropertyAction,
                           propertyTypeReference,
                           isOpenPropertyType,
                           complexValueProjectedProperties));
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                this.WriteCollectionValueProperty(
                    collectionValue,
                    propertyName,
                    isTopLevel,
                    isWritingCollection,
                    beforePropertyAction,
                    propertyTypeReference,
                    isOpenPropertyType);

                return(true);
            }

            // If the value isn't one of the value types tested for already, it must be a non-null primitive or enum type.
            this.WritePropertyStart(beforePropertyAction, property, isWritingCollection, isTopLevel);
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = property.ODataValue.GetAnnotation <SerializationTypeNameAnnotation>();
            ODataEnumValue enumValue = value as ODataEnumValue;

            if (enumValue != null)
            {
                this.WriteEnumValue(enumValue, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation);
            }
            else
            {
                this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference, serializationTypeNameAnnotation);
            }

            this.WritePropertyEnd();
            return(true);
        }
        /// <summary>
        /// Writes the __metadata property and its content for an entry
        /// </summary>
        /// <param name="entry">The entry for which to write the metadata.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <param name="entryEntityType">The entity type of the entry to write.</param>
        /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use.</param>
        internal void WriteEntryMetadata(
            ODataEntry entry,
            ProjectedPropertiesAnnotation projectedProperties,
            IEdmEntityType entryEntityType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(entry != null, "entry != null");

            // Write the "__metadata" for the entry
            this.JsonWriter.WriteName(JsonConstants.ODataMetadataName);
            this.JsonWriter.StartObjectScope();

            // Write the "id": "Entity Id"
            string id = entry.Id;

            if (id != null)
            {
                this.JsonWriter.WriteName(JsonConstants.ODataEntryIdName);
                this.JsonWriter.WriteValue(id);
            }

            // Write the "uri": "edit/read-link-uri"
            Uri uriValue = entry.EditLink ?? entry.ReadLink;

            if (uriValue != null)
            {
                this.JsonWriter.WriteName(JsonConstants.ODataMetadataUriName);
                this.JsonWriter.WriteValue(this.UriToAbsoluteUriString(uriValue));
            }

            // Write the "etag": "ETag value"
            // TODO: if this is a top-level entry also put the ETag into the headers.
            string etag = entry.ETag;

            if (etag != null)
            {
                this.WriteETag(JsonConstants.ODataMetadataETagName, etag);
            }

            // Write the "type": "typename"
            string typeName = this.VerboseJsonOutputContext.TypeNameOracle.GetEntryTypeNameForWriting(entry);

            if (typeName != null)
            {
                this.JsonWriter.WriteName(JsonConstants.ODataMetadataTypeName);
                this.JsonWriter.WriteValue(typeName);
            }

            // Write MLE metadata
            ODataStreamReferenceValue mediaResource = entry.MediaResource;

            if (mediaResource != null)
            {
                WriterValidationUtils.ValidateStreamReferenceValue(mediaResource, true);
                this.WriteStreamReferenceValueContent(mediaResource);
            }

            // write "actions" metadata
            IEnumerable <ODataAction> actions = entry.Actions;

            if (actions != null)
            {
                this.WriteOperations(actions.Cast <ODataOperation>(), JsonConstants.ODataActionsMetadataName, /*isAction*/ true, /*writingJsonLight*/ false);
            }

            // write "functions" metadata
            IEnumerable <ODataFunction> functions = entry.Functions;

            if (functions != null)
            {
                this.WriteOperations(functions.Cast <ODataOperation>(), JsonConstants.ODataFunctionsMetadataName, /*isAction*/ false, /*writingJsonLight*/ false);
            }

            // Write properties metadata
            // For now only association links are supported here
            IEnumerable <ODataAssociationLink> associationLinks = entry.AssociationLinks;

            if (associationLinks != null)
            {
                bool firstAssociationLink = true;

                foreach (ODataAssociationLink associationLink in associationLinks)
                {
                    ValidationUtils.ValidateAssociationLinkNotNull(associationLink);
                    if (projectedProperties.ShouldSkipProperty(associationLink.Name))
                    {
                        continue;
                    }

                    if (firstAssociationLink)
                    {
                        // Write the "properties": {
                        this.JsonWriter.WriteName(JsonConstants.ODataMetadataPropertiesName);
                        this.JsonWriter.StartObjectScope();

                        firstAssociationLink = false;
                    }

                    this.ValidateAssociationLink(associationLink, entryEntityType);
                    this.WriteAssociationLink(associationLink, duplicatePropertyNamesChecker);
                }

                if (!firstAssociationLink)
                {
                    // Close the "properties" object
                    this.JsonWriter.EndObjectScope();
                }
            }

            // Close the __metadata object scope
            this.JsonWriter.EndObjectScope();
        }
 internal void WriteProperties(IEdmStructuredType owningType, IEnumerable <ODataProperty> properties, bool isComplexValue, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
 {
     if (properties != null)
     {
         foreach (ODataProperty property in properties)
         {
             this.WriteProperty(property, owningType, !isComplexValue, duplicatePropertyNamesChecker, projectedProperties);
         }
     }
 }
        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;
        }
 private void WriteProperty(ODataProperty property, IEdmStructuredType owningType, bool allowStreamProperty, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
 {
     WriterValidationUtils.ValidatePropertyNotNull(property);
     if (!projectedProperties.ShouldSkipProperty(property.Name))
     {
         WriterValidationUtils.ValidateProperty(property);
         duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
         IEdmProperty expectedProperty = WriterValidationUtils.ValidatePropertyDefined(property.Name, owningType);
         if (((expectedProperty != null) && expectedProperty.Type.IsSpatial()) || ((expectedProperty == null) && (property.Value is ISpatial)))
         {
             ODataVersionChecker.CheckSpatialValue(base.Version);
         }
         base.JsonWriter.WriteName(property.Name);
         object obj2 = property.Value;
         if (obj2 == null)
         {
             WriterValidationUtils.ValidateNullPropertyValue(expectedProperty, base.MessageWriterSettings.WriterBehavior, base.Model);
             base.JsonWriter.WriteValue((string)null);
         }
         else
         {
             bool isOpenPropertyType = ((owningType != null) && owningType.IsOpen) && (expectedProperty == null);
             if (isOpenPropertyType)
             {
                 ValidationUtils.ValidateOpenPropertyValue(property.Name, obj2);
             }
             IEdmTypeReference propertyTypeReference = (expectedProperty == null) ? null : expectedProperty.Type;
             ODataComplexValue complexValue          = obj2 as ODataComplexValue;
             if (complexValue != null)
             {
                 this.WriteComplexValue(complexValue, propertyTypeReference, isOpenPropertyType, base.CreateDuplicatePropertyNamesChecker(), null);
             }
             else
             {
                 ODataCollectionValue collectionValue = obj2 as ODataCollectionValue;
                 if (collectionValue != null)
                 {
                     ODataVersionChecker.CheckCollectionValueProperties(base.Version, property.Name);
                     this.WriteCollectionValue(collectionValue, propertyTypeReference, isOpenPropertyType);
                 }
                 else if (obj2 is ODataStreamReferenceValue)
                 {
                     if (!allowStreamProperty)
                     {
                         throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(property.Name));
                     }
                     WriterValidationUtils.ValidateStreamReferenceProperty(property, expectedProperty, base.Version, base.WritingResponse);
                     this.WriteStreamReferenceValue((ODataStreamReferenceValue)property.Value);
                 }
                 else
                 {
                     this.WritePrimitiveValue(obj2, null, propertyTypeReference);
                 }
             }
         }
     }
 }
        /// <summary>
        /// Writes a property with a complex value in ATOM format.
        /// </summary>
        /// <param name="complexValue">The complex value to write.</param>
        /// <param name="propertyName">The name of the property being written.</param>
        /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        /// <param name="beforeValueAction">Action called before the complex value is written, if it's actually written.</param>
        /// <param name="propertyTypeReference">The type information for the property being written.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="complexValueProjectedProperties">Set of projected properties, or null if all properties should be written.</param>
        /// <returns>true if anything was written, false otherwise.</returns>
        private bool WriteComplexValueProperty(
            ODataComplexValue complexValue,
            string propertyName,
            bool isTopLevel,
            bool isWritingCollection,
            Action beforeValueAction,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            ProjectedPropertiesAnnotation complexValueProjectedProperties)
        {
            // Complex properties are written recursively.
            DuplicatePropertyNamesChecker complexValuePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
            if (isTopLevel)
            {
                // Top-level property must always write the property element
                Debug.Assert(complexValueProjectedProperties == null, "complexValueProjectedProperties == null");
                this.WritePropertyStart(beforeValueAction, propertyName, complexValue, isWritingCollection, /*isTopLevel*/ true);

                this.AssertRecursionDepthIsZero();
                this.WriteComplexValue(
                    complexValue,
                    propertyTypeReference,
                    isOpenPropertyType,
                    isWritingCollection,
                    null /* beforeValueAction */,
                    null /* afterValueAction */,
                    complexValuePropertyNamesChecker,
                    null /* collectionValidator */,
                    null /* projectedProperties */);
                this.AssertRecursionDepthIsZero();
                this.WritePropertyEnd();
                return true;
            }

            return this.WriteComplexValue(
                complexValue,
                propertyTypeReference,
                isOpenPropertyType,
                isWritingCollection,
                () => this.WritePropertyStart(beforeValueAction, propertyName, complexValue, isWritingCollection, /*isTopLevel*/ false),
                this.WritePropertyEnd,
                complexValuePropertyNamesChecker,
                null /* collectionValidator */,
                complexValueProjectedProperties);
        }
        /// <summary>
        /// Writes property names and value pairs.
        /// </summary>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry (or null if not metadata is available).</param>
        /// <param name="properties">The enumeration of properties to write out.</param>
        /// <param name="isComplexValue">
        /// Whether the properties are being written for complex value. Also used for detecting whether stream properties
        /// are allowed as named stream properties should only be defined on ODataEntry instances
        /// </param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        internal void WriteProperties(
            IEdmStructuredType owningType,
            IEnumerable<ODataProperty> properties,
            bool isComplexValue,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            if (properties == null)
            {
                return;
            }

            foreach (ODataProperty property in properties)
            {
                this.WriteProperty(
                    property,
                    owningType,
                    false /* isTopLevel */,
                    !isComplexValue,
                    duplicatePropertyNamesChecker,
                    projectedProperties);
            }
        }
        /// <summary>
        /// Writes a name/value pair for a property.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The type owning the property (or null if no metadata is available).</param>
        /// <param name="allowStreamProperty">Should pass in true if we are writing a property of an ODataEntry instance, false otherwise.
        /// Named stream properties should only be defined on ODataEntry instances.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);
            if (projectedProperties.ShouldSkipProperty(property.Name))
            {
                return;
            }

            WriterValidationUtils.ValidateProperty(property);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(property.Name, owningType);

            // 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.
            if ((edmProperty != null && edmProperty.Type.IsSpatial()) ||
                (edmProperty == null && property.Value is System.Spatial.ISpatial))
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            this.JsonWriter.WriteName(property.Name);
            object value = property.Value;

            if (value == null)
            {
                WriterValidationUtils.ValidateNullPropertyValue(edmProperty, this.MessageWriterSettings.WriterBehavior, this.Model);
                this.JsonWriter.WriteValue(null);
            }
            else
            {
                bool isOpenPropertyType = owningType != null && owningType.IsOpen && edmProperty == null;
                if (isOpenPropertyType)
                {
                    ValidationUtils.ValidateOpenPropertyValue(property.Name, value);
                }

                IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;
                ODataComplexValue complexValue          = value as ODataComplexValue;
                if (complexValue != null)
                {
                    this.WriteComplexValue(
                        complexValue,
                        propertyTypeReference,
                        isOpenPropertyType,
                        this.CreateDuplicatePropertyNamesChecker(),
                        /*collectionValidator*/ null);
                }
                else
                {
                    ODataCollectionValue collectionValue = value as ODataCollectionValue;
                    if (collectionValue != null)
                    {
                        ODataVersionChecker.CheckCollectionValueProperties(this.Version, property.Name);
                        this.WriteCollectionValue(
                            collectionValue,
                            propertyTypeReference,
                            isOpenPropertyType);
                    }
                    else
                    {
                        ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;
                        if (streamReferenceValue != null)
                        {
                            if (!allowStreamProperty)
                            {
                                throw new ODataException(o.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(property.Name));
                            }

                            Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                            WriterValidationUtils.ValidateStreamReferenceProperty(property, edmProperty, this.Version, this.WritingResponse);
                            this.WriteStreamReferenceValue((ODataStreamReferenceValue)property.Value);
                        }
                        else
                        {
                            this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference);
                        }
                    }
                }
            }
        }
        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.ODataAnnotationWriter.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);
            }
        }
        /// <summary>
        /// Writes a stream property to the ATOM payload
        /// </summary>
        /// <param name="streamProperty">The stream property to create the payload for.</param>
        /// <param name="owningType">The <see cref="IEdmEntityType"/> instance for which the stream property defined on.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        internal void WriteStreamProperty(
            ODataProperty streamProperty,
            IEdmEntityType owningType,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(streamProperty != null, "Stream property must not be null.");
            Debug.Assert(streamProperty.Value != null, "The media resource of the stream property must not be null.");

            WriterValidationUtils.ValidatePropertyNotNull(streamProperty);
            string propertyName = streamProperty.Name;
            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            WriterValidationUtils.ValidateProperty(streamProperty);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(streamProperty);
            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(streamProperty.Name, owningType);
            WriterValidationUtils.ValidateStreamReferenceProperty(streamProperty, edmProperty, this.Version, this.WritingResponse);
            ODataStreamReferenceValue streamReferenceValue = (ODataStreamReferenceValue)streamProperty.Value;
            if (owningType != null && owningType.IsOpen && edmProperty == null)
            {
                ValidationUtils.ValidateOpenPropertyValue(streamProperty.Name, streamReferenceValue);
            }

            AtomStreamReferenceMetadata streamReferenceMetadata = streamReferenceValue.GetAnnotation<AtomStreamReferenceMetadata>();
            string contentType = streamReferenceValue.ContentType;
            string linkTitle = streamProperty.Name;

            Uri readLink = streamReferenceValue.ReadLink;
            if (readLink != null)
            {
                string readLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, false);

                AtomLinkMetadata readLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.SelfLink;
                AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(readLinkMetadata, readLinkRelation, readLink, linkTitle, contentType);
                this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, null /* etag */);
            }

            Uri editLink = streamReferenceValue.EditLink;
            if (editLink != null)
            {
                string editLinkRelation = AtomUtils.ComputeStreamPropertyRelation(streamProperty, true);

                AtomLinkMetadata editLinkMetadata = streamReferenceMetadata == null ? null : streamReferenceMetadata.EditLink;
                AtomLinkMetadata mergedMetadata = ODataAtomWriterMetadataUtils.MergeLinkMetadata(editLinkMetadata, editLinkRelation, editLink, linkTitle, contentType);
                this.atomEntryMetadataSerializer.WriteAtomLink(mergedMetadata, streamReferenceValue.ETag);
            }
        }
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            this.WriterValidator.ValidatePropertyNotNull(property);

            string propertyName = property.Name;
            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            this.WriterValidator.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);

            WriteInstanceAnnotation(property, isTopLevel);

            bool throwOnMissingProperty = this.JsonLightOutputContext.MessageWriterSettings.EnableFullValidation && !this.WritingResponse;
            IEdmProperty edmProperty = this.WriterValidator.ValidatePropertyDefined(
                propertyName,
                owningType,
                throwOnMissingProperty);

            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.");
                this.WriterValidator.ValidateStreamReferenceProperty(property, edmProperty, this.WritingResponse);
                this.WriteStreamReferenceProperty(propertyName, streamReferenceValue);
                return;
            }

            if (value is ODataNullValue || value == null)
            {
                this.WriteNullProperty(property, propertyTypeReference, isTopLevel);
                return;
            }

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

            ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue;
            if (primitiveValue != null)
            {
                this.WritePrimitiveProperty(property, primitiveValue, propertyTypeReference, isTopLevel, isOpenPropertyType);
                return;
            }

            ODataComplexValue complexValue = value as ODataComplexValue;
            if (complexValue != null)
            {
                this.WriteComplexProperty(property, complexValue, propertyTypeReference, isTopLevel, isOpenPropertyType);
                return;
            }

            ODataEnumValue enumValue = value as ODataEnumValue;
            if (enumValue != null)
            {
                this.WriteEnumProperty(property, enumValue, propertyTypeReference, isTopLevel, isOpenPropertyType);
                return;
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;
            if (collectionValue != null)
            {
                this.WriteCollectionProperty(property, collectionValue, propertyTypeReference, isTopLevel, isOpenPropertyType);
                return;
            }
            else
            {
                ODataUntypedValue untypedValue = value as ODataUntypedValue;
                this.WriteUntypedProperty(property, untypedValue, isTopLevel);
                return;
            }
        }