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