public void ReadInstanceAnnotationValueWillReadNullValueWithoutODataType() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@odata.NullAnnotation\":null}"); AdvanceReaderToFirstPropertyValue(deserializer.JsonReader); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); deserializer.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, "odata.NullAnnotation").Should().BeNull(); }
/// <summary> /// Read a service document. /// This method reads the service document from the input and returns /// an <see cref="ODataServiceDocument"/> that represents the read service document. /// </summary> /// <returns>A task which returns an <see cref="ODataServiceDocument"/> representing the read service document.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None: assumes that the JSON reader has not been used yet. /// Post-Condition: JsonNodeType.EndOfInput /// </remarks> internal Task<ODataServiceDocument> ReadServiceDocumentAsync() { Debug.Assert(this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None, the reader must not have been used yet."); this.JsonReader.AssertNotBuffering(); // We use this to store annotations and check for duplicate annotation names, but we don't really store properties in it. DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); // Position the reader on the first node return this.ReadPayloadStartAsync( ODataPayloadKind.ServiceDocument, duplicatePropertyNamesChecker, /*isReadingNestedPayload*/false, /*allowEmptyPayload*/false) .FollowOnSuccessWith(t => { ODataServiceDocument serviceDocument = this.ReadServiceDocumentImplementation(duplicatePropertyNamesChecker); // Read the end of the response. this.ReadPayloadEnd(/*isReadingNestedPayload*/ false); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndOfInput, "Post-Condition: expected JsonNodeType.EndOfInput"); this.JsonReader.AssertNotBuffering(); return serviceDocument; }); }
/// <summary> /// Read a service document. /// This method reads the service document from the input and returns /// an <see cref="ODataWorkspace"/> that represents the read service document. /// </summary> /// <returns>An <see cref="ODataWorkspace"/> representing the read service document.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None: assumes that the JSON reader has not been used yet. /// Post-Condition: JsonNodeType.EndOfInput /// </remarks> internal ODataWorkspace ReadServiceDocument() { DebugUtils.CheckNoExternalCallers(); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None, the reader must not have been used yet."); this.JsonReader.AssertNotBuffering(); // We use this to store annotations and check for duplicate annotation names, but we don't really store properties in it. DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); // Position the reader on the first node this.ReadPayloadStart( ODataPayloadKind.ServiceDocument, duplicatePropertyNamesChecker, /*isReadingNestedPayload*/ false, /*allowEmptyPayload*/ false); ODataWorkspace resultWorkspace = this.ReadServiceDocumentImplementation(duplicatePropertyNamesChecker); // Read the end of the response. this.ReadPayloadEnd(/*isReadingNestedPayload*/ false); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndOfInput, "Post-Condition: expected JsonNodeType.EndOfInput"); this.JsonReader.AssertNotBuffering(); return(resultWorkspace); }
public void DuplicatePropertyCustomAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.AddCustomPropertyAnnotation("property", "custom.name"); action.ShouldNotThrow(); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationForPropertyNotAllowed("custom.name", "property")); }
public void OnlyCustomAnnotationsForPropertyAddedShouldReturnNull() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); duplicateChecker.AddCustomPropertyAnnotation("property", "custom.annotation"); duplicateChecker.AddCustomPropertyAnnotation("property", "custom.annotation2"); duplicateChecker.GetODataPropertyAnnotations("property").Should().BeNull(); }
public void DuplicatePropertyODataAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.AddODataPropertyAnnotation("property", JsonLightConstants.ODataAnnotationNamespacePrefix + "name", null); action.ShouldNotThrow(); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationForPropertyNotAllowed(JsonLightConstants.ODataAnnotationNamespacePrefix + "name", "property")); }
/// <summary> /// Read a top-level error. /// </summary> /// <returns>An <see cref="ODataError"/> representing the read error.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None - The reader must not have been used yet. /// Post-Condition: JsonNodeType.EndOfInput /// </remarks> internal ODataError ReadTopLevelError() { DebugUtils.CheckNoExternalCallers(); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None, the reader must not have been used yet."); Debug.Assert(!this.JsonReader.DisableInStreamErrorDetection, "!JsonReader.DisableInStreamErrorDetection"); this.JsonReader.AssertNotBuffering(); // prevent the buffering JSON reader from detecting in-stream errors - we read the error ourselves // to throw proper exceptions this.JsonReader.DisableInStreamErrorDetection = true; // We use this to store annotations and check for duplicate annotation names, but we don't really store properties in it. DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); try { // Position the reader on the first node this.ReadPayloadStart( ODataPayloadKind.Error, duplicatePropertyNamesChecker, /*isReadingNestedPayload*/ false, /*allowEmptyPayload*/ false); ODataError result = this.ReadTopLevelErrorImplementation(); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndOfInput, "Post-Condition: JsonNodeType.EndOfInput"); this.JsonReader.AssertNotBuffering(); return(result); } finally { this.JsonReader.DisableInStreamErrorDetection = false; } }
/// <summary> /// Read a set of top-level entity reference links. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use for the top-level scope.</param> /// <returns>An <see cref="ODataEntityReferenceLinks"/> representing the read links.</returns> private ODataEntityReferenceLinks ReadEntityReferenceLinksImplementation(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); ODataEntityReferenceLinks entityReferenceLinks = new ODataEntityReferenceLinks(); this.ReadEntityReferenceLinksAnnotations(entityReferenceLinks, duplicatePropertyNamesChecker, /*forLinksStart*/ true); // Read the start of the content array of the links this.JsonReader.ReadStartArray(); List <ODataEntityReferenceLink> links = new List <ODataEntityReferenceLink>(); DuplicatePropertyNamesChecker linkDuplicatePropertyNamesChecker = this.JsonLightInputContext.CreateDuplicatePropertyNamesChecker(); while (this.JsonReader.NodeType != JsonNodeType.EndArray) { // read another link ODataEntityReferenceLink entityReferenceLink = this.ReadSingleEntityReferenceLink(linkDuplicatePropertyNamesChecker, /*topLevel*/ false); links.Add(entityReferenceLink); linkDuplicatePropertyNamesChecker.Clear(); } this.JsonReader.ReadEndArray(); this.ReadEntityReferenceLinksAnnotations(entityReferenceLinks, duplicatePropertyNamesChecker, /*forLinksStart*/ false); this.JsonReader.ReadEndObject(); entityReferenceLinks.Links = new ReadOnlyEnumerable <ODataEntityReferenceLink>(links); return(entityReferenceLinks); }
/// <summary> /// Implementation of the collection reader logic when in state 'Start'. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the top-level scope.</param> /// <returns>true if more items can be read from the reader; otherwise false.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None: assumes that the JSON reader has not been used yet when not reading a nested payload. /// Post-Condition: The reader is positioned on the first node of the first item or the EndArray node of an empty item array /// </remarks> private bool ReadAtStartImplementationSynchronously(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); IEdmTypeReference actualItemTypeReference; this.ExpectedItemTypeReference = ReaderValidationUtils.ValidateCollectionContextUriAndGetPayloadItemTypeReference( this.jsonLightCollectionDeserializer.ContextUriParseResult, this.ExpectedItemTypeReference); // read the start of the collection until we find the content array for top-level collections ODataCollectionStart collectionStart = this.jsonLightCollectionDeserializer.ReadCollectionStart( duplicatePropertyNamesChecker, this.IsReadingNestedPayload, this.ExpectedItemTypeReference, out actualItemTypeReference); if (actualItemTypeReference != null) { this.ExpectedItemTypeReference = actualItemTypeReference; } this.jsonLightCollectionDeserializer.JsonReader.ReadStartArray(); this.EnterScope(ODataCollectionReaderState.CollectionStart, collectionStart); return(true); }
/// <summary> /// Read a set of top-level entity reference links. /// </summary> /// <returns>A task which returns an <see cref="ODataEntityReferenceLinks"/> representing the read links.</returns> internal Task <ODataEntityReferenceLinks> ReadEntityReferenceLinksAsync() { Debug.Assert(this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None, the reader must not have been used yet."); this.JsonReader.AssertNotBuffering(); // We use this to store annotations and check for duplicate annotation names, but we don't really store properties in it. DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); return(this.ReadPayloadStartAsync( ODataPayloadKind.EntityReferenceLinks, duplicatePropertyNamesChecker, /*isReadingNestedPayload*/ false, /*allowEmptyPayload*/ false) .FollowOnSuccessWith(t => { ODataEntityReferenceLinks entityReferenceLinks = this.ReadEntityReferenceLinksImplementation(duplicatePropertyNamesChecker); this.ReadPayloadEnd(/*isReadingNestedPayload*/ false); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndOfInput, "Post-Condition: expected JsonNodeType.EndOfInput"); this.JsonReader.AssertNotBuffering(); return entityReferenceLinks; })); }
private ODataCollectionValue ReadCollectionValue(IEdmCollectionTypeReference collectionTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { this.IncreaseRecursionDepth(); ODataCollectionValue value2 = new ODataCollectionValue { TypeName = (collectionTypeReference == null) ? payloadTypeName : collectionTypeReference.ODataFullName() }; if (serializationTypeNameAnnotation != null) { value2.SetAnnotation <SerializationTypeNameAnnotation>(serializationTypeNameAnnotation); } base.XmlReader.MoveToElement(); List <object> sourceEnumerable = new List <object>(); if (!base.XmlReader.IsEmptyElement) { base.XmlReader.ReadStartElement(); IEdmTypeReference expectedTypeReference = (collectionTypeReference == null) ? null : collectionTypeReference.ElementType(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); CollectionWithoutExpectedTypeValidator collectionValidator = null; if (collectionTypeReference == null) { string itemTypeNameFromCollection = (payloadTypeName == null) ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); } do { switch (base.XmlReader.NodeType) { case XmlNodeType.Element: if (base.XmlReader.NamespaceEquals(base.XmlReader.ODataNamespace)) { if (!base.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(Microsoft.Data.OData.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(base.XmlReader.LocalName, base.XmlReader.ODataNamespace)); } object item = this.ReadNonEntityValueImplementation(expectedTypeReference, duplicatePropertyNamesChecker, collectionValidator, true, false); base.XmlReader.Read(); ValidationUtils.ValidateCollectionItem(item, false); sourceEnumerable.Add(item); } else { base.XmlReader.Skip(); } break; case XmlNodeType.EndElement: break; default: base.XmlReader.Skip(); break; } }while (base.XmlReader.NodeType != XmlNodeType.EndElement); } value2.Items = new ReadOnlyEnumerable(sourceEnumerable); this.DecreaseRecursionDepth(); return(value2); }
public void DuplicateInstanceCustomAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.MarkPropertyAsProcessed("custom.name"); action.ShouldNotThrow(); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationNotAllowed("custom.name")); }
/// <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> /// Parses JSON object property starting with the current position of the JSON reader. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use, it will also store the property annotations found.</param> /// <param name="readPropertyAnnotationValue">Function called to read property annotation value.</param> /// <param name="handleProperty">Function callback to handle to resule of parse property.</param> internal void ProcessProperty( DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func <string, object> readPropertyAnnotationValue, Action <PropertyParsingResult, string> handleProperty) { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); Debug.Assert(readPropertyAnnotationValue != null, "readPropertyAnnotationValue != null"); Debug.Assert(handleProperty != null, "handleProperty != null"); this.AssertJsonCondition(JsonNodeType.Property); string propertyName; PropertyParsingResult propertyParsingResult = this.ParseProperty(duplicatePropertyNamesChecker, readPropertyAnnotationValue, out propertyName); while (propertyParsingResult == PropertyParsingResult.CustomInstanceAnnotation && this.ShouldSkipCustomInstanceAnnotation(propertyName)) { // Make sure there's no duplicated instance annotation name even though we are skipping over it. duplicatePropertyNamesChecker.MarkPropertyAsProcessed(propertyName); // Skip over the instance annotation value and don't report it to the OM. this.JsonReader.SkipValue(); propertyParsingResult = this.ParseProperty(duplicatePropertyNamesChecker, readPropertyAnnotationValue, out propertyName); } handleProperty(propertyParsingResult, propertyName); if (propertyParsingResult != PropertyParsingResult.EndOfObject) { duplicatePropertyNamesChecker.MarkPropertyAsProcessed(propertyName); } }
/// <summary> /// Constructor. /// </summary> /// <param name="atomInputContext">The ATOM input context to read from.</param> internal ODataAtomCollectionDeserializer(ODataAtomInputContext atomInputContext) : base(atomInputContext) { DebugUtils.CheckNoExternalCallers(); this.duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); }
/// <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> /// 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> /// Constructor. /// </summary> /// <param name="jsonInputContext">The JSON input context to read from.</param> internal ODataVerboseJsonCollectionDeserializer(ODataVerboseJsonInputContext jsonInputContext) : base(jsonInputContext) { DebugUtils.CheckNoExternalCallers(); this.duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); }
/// <summary> /// Read the start of the top-level data wrapper in JSON responses. /// </summary> /// <param name="payloadKind">The kind of payload we are reading; this guides the parsing of the context URI.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker.</param> /// <param name="isReadingNestedPayload">true if we are deserializing a nested payload, e.g. an entry, a feed or a collection within a parameters payload.</param> /// <param name="allowEmptyPayload">true if we allow a comletely empty payload; otherwise false.</param> /// <returns>The parsed context URI.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None: assumes that the JSON reader has not been used yet when not reading a nested payload. /// Post-Condition: The reader is positioned on the first property of the payload after having read (or skipped) the context URI property. /// Or the reader is positioned on an end-object node if there are no properties (other than the context URI which is required in responses and optional in requests). /// </remarks> internal Task ReadPayloadStartAsync( ODataPayloadKind payloadKind, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool isReadingNestedPayload, bool allowEmptyPayload) { this.JsonReader.AssertNotBuffering(); Debug.Assert(isReadingNestedPayload || this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: JSON reader must not have been used yet when not reading a nested payload."); return(TaskUtils.GetTaskForSynchronousOperation(() => { string contextUriAnnotationValue = this.ReadPayloadStartImplementation( payloadKind, duplicatePropertyNamesChecker, isReadingNestedPayload, allowEmptyPayload); // The context URI is only recognized in non-error response top-level payloads. // If the payload is nested (for example when we read URL literals) we don't recognize the context URI. // Top-level error payloads don't need and use the context URI. if (!isReadingNestedPayload && payloadKind != ODataPayloadKind.Error && contextUriAnnotationValue != null) { this.contextUriParseResult = ODataJsonLightContextUriParser.Parse( this.Model, contextUriAnnotationValue, payloadKind, this.MessageReaderSettings.ReaderBehavior, this.JsonLightInputContext.ReadingResponse); } #if DEBUG this.contextUriParseResultReady = true; #endif })); }
public void DuplicatePropertyODataAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.AddODataPropertyAnnotation("property", JsonLightConstants.ODataAnnotationNamespacePrefix + "name", null); action.ShouldNotThrow(); action.ShouldThrow <ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationForPropertyNotAllowed(JsonLightConstants.ODataAnnotationNamespacePrefix + "name", "property")); }
public void DuplicatePropertyCustomAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.AddCustomPropertyAnnotation("property", "custom.name"); action.ShouldNotThrow(); action.ShouldThrow <ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationForPropertyNotAllowed("custom.name", "property")); }
public void DuplicateInstanceCustomAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.MarkPropertyAsProcessed("custom.name"); action.ShouldNotThrow(); action.ShouldThrow <ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationNotAllowed("custom.name")); }
internal void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType) { this.IncreaseRecursionDepth(); base.JsonWriter.StartObjectScope(); string typeName = collectionValue.TypeName; IEdmCollectionTypeReference type = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string itemTypeNameFromCollection = null; if (typeName != null) { itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation annotation = collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (typeName != null) { base.JsonWriter.WriteName("__metadata"); base.JsonWriter.StartObjectScope(); base.JsonWriter.WriteName("type"); base.JsonWriter.WriteValue(typeName); base.JsonWriter.EndObjectScope(); } base.JsonWriter.WriteDataArrayName(); base.JsonWriter.StartArrayScope(); IEnumerable items = collectionValue.Items; if (items != null) { IEdmTypeReference propertyTypeReference = (type == null) ? null : type.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object obj2 in items) { ValidationUtils.ValidateCollectionItem(obj2, false); ODataComplexValue complexValue = obj2 as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue(complexValue, propertyTypeReference, false, duplicatePropertyNamesChecker, collectionValidator); duplicatePropertyNamesChecker.Clear(); } else { this.WritePrimitiveValue(obj2, collectionValidator, propertyTypeReference); } } } base.JsonWriter.EndArrayScope(); base.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }
private void WriteAssociationLink(ODataAssociationLink associationLink, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(associationLink); base.JsonWriter.WriteName(associationLink.Name); base.JsonWriter.StartObjectScope(); base.JsonWriter.WriteName("associationuri"); base.JsonWriter.WriteValue(base.UriToAbsoluteUriString(associationLink.Url)); base.JsonWriter.EndObjectScope(); }
/// <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); } }
public void ReadInstanceAnnotationValueWillReadNonJsonNativePrimitiveTypesWithoutODataTypeAsStringValue() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@Custom.GuidAnnotation\":\"00000000-0000-0000-0000-000000000000\"}"); AdvanceReaderToFirstPropertyValue(deserializer.JsonReader); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); deserializer.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, "Custom.GuidAnnotation").Should().Be("00000000-0000-0000-0000-000000000000"); }
public void ReadInstanceAnnotationValueWillReadAsCorrectTypePrimitiveTypesWithODataTypeAnnotation() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@Custom.GuidAnnotation\":\"00000000-0000-0000-0000-000000000000\"}"); AdvanceReaderToFirstPropertyValue(deserializer.JsonReader); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); duplicatePropertyNamesChecker.AddODataPropertyAnnotation("Custom.GuidAnnotation", "odata.type", "Edm.Guid"); deserializer.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, "Custom.GuidAnnotation").Should().Be(Guid.Empty); }
public void ReadInstanceAnnotationValueWillFailIfODataTypeAnnotationIsMissingForCollectionValue() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@OData.CollectionAnnotation\":[]}"); AdvanceReaderToFirstPropertyValue(deserializer.JsonReader); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); Action action = () => deserializer.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, "OData.CollectionAnnotation"); action.ShouldThrow <ODataException>().WithMessage(ErrorStrings.ReaderValidationUtils_ValueWithoutType); }
public void ReadEntryInstanceAnnotationShouldReadCustomInstanceAnnotationValue() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@custom.Int32Annotation\":123}"); AdvanceReaderToFirstPropertyValue(deserializer.JsonReader); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); object value = deserializer.ReadEntryInstanceAnnotation("custom.Int32Annotation", false /*anyPropertyFound*/, true /*typeAnnotationFound*/, duplicatePropertyNamesChecker); Assert.AreEqual(123, value); }
/// <summary> /// Reads the end of a collection; this includes collection-level instance annotations. /// </summary> /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param> /// <remarks> /// Pre-Condition: EndArray node: End of the collection content array /// Post-Condition: EndOfInput: All of the collection payload has been consumed. /// </remarks> internal void ReadCollectionEnd(bool isReadingNestedPayload) { Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "Pre-condition: JsonNodeType.EndArray"); this.JsonReader.AssertNotBuffering(); this.JsonReader.ReadEndArray(); if (!isReadingNestedPayload) { // Create a new duplicate property names checker object here; we don't have to use the one from reading the // collection start since we don't allow any annotations/properties after the collection property. DuplicatePropertyNamesChecker collectionEndDuplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); // Fail on anything after the collection that is not a custom instance annotation while (this.JsonReader.NodeType == JsonNodeType.Property) { this.ProcessProperty( collectionEndDuplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { // This method will allow and skip over any custom annotations, but will not report them as enum values, so any result we get other than EndOfObject indicates a malformed payload. switch (propertyParsingResult) { case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.ODataInstanceAnnotation: if (!IsValidODataAnnotationOfCollection(propertyName)) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionEnd(propertyName)); } this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithoutValue: // fall through case PropertyParsingResult.PropertyWithValue: // fall through case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionEnd(propertyName)); case PropertyParsingResult.EndOfObject: break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionEnd)); } }); } // read the end-object node of the value containing the 'value' property this.JsonReader.ReadEndObject(); } }
public void ReadTopLevelFeedAnnotationsForFeedEndAndBufferingShouldSkipAllValues() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@custom.before\":123,\"value\":[],\"@custom.after\":456}"); AdvanceReaderToFirstProperty(deserializer.JsonReader); var feed = new ODataFeed(); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); deserializer.ReadTopLevelFeedAnnotations(feed, duplicatePropertyNamesChecker, false /*forFeedStart*/, true /*readAllFeedProperties*/); feed.InstanceAnnotations.Should().BeEmpty(); }
public void ReadTopLevelFeedAnnotationsForFeedEndAndNonBufferingShouldSkipInstanceAnnotationsBasedOnReaderSettings() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@custom.after\":456}", shouldReadAndValidateCustomInstanceAnnotations: false); AdvanceReaderToFirstProperty(deserializer.JsonReader); var feed = new ODataFeed(); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); deserializer.ReadTopLevelFeedAnnotations(feed, duplicatePropertyNamesChecker, false /*forFeedStart*/, false /*readAllFeedProperties*/); Assert.AreEqual(0, feed.InstanceAnnotations.Count); }
public void AnnotationsForPropertyShouldBeStored() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); duplicateChecker.AddODataPropertyAnnotation("property", JsonLightConstants.ODataAnnotationNamespacePrefix + "one", 1); duplicateChecker.AddODataPropertyAnnotation("property", JsonLightConstants.ODataAnnotationNamespacePrefix + "two", "Two"); duplicateChecker.GetODataPropertyAnnotations("property").Should().Equal(new Dictionary<string, object>() { { JsonLightConstants.ODataAnnotationNamespacePrefix + "one", 1 }, { JsonLightConstants.ODataAnnotationNamespacePrefix + "two", "Two" } }); }
public void ReadInstanceAnnotationValueWhenODataTypeAnnotationIsMissingForCollectionValue() { var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@OData.CollectionAnnotation\":[]}"); AdvanceReaderToFirstPropertyValue(deserializer.JsonReader); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/); object tmp = deserializer.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, "OData.CollectionAnnotation"); tmp.As <ODataCollectionValue>().Items.Cast <string>().Count().Should().Be(0); tmp.As <ODataCollectionValue>().TypeName.ShouldBeEquivalentTo(null); }
/// <summary> /// Implementation of the reader logic when in state 'Start'. /// </summary> /// <returns>true if more items can be read from the reader; otherwise false.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.None: assumes that the JSON reader has not been used yet. /// Post-Condition: When the new state is Value, the reader is positioned at the closing '}' or at the name of the next parameter. /// When the new state is Entry, the reader is positioned at the starting '{' of the entry payload. /// When the new state is Feed or Collection, the reader is positioned at the starting '[' of the feed or collection payload. /// </remarks> protected override bool ReadAtStartImplementation() { Debug.Assert(this.State == ODataParameterReaderState.Start, "this.State == ODataParameterReaderState.Start"); Debug.Assert(this.jsonLightParameterDeserializer.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None"); // We use this to store annotations and check for duplicate annotation names, but we don't really store properties in it. this.duplicatePropertyNamesChecker = this.jsonLightInputContext.CreateDuplicatePropertyNamesChecker(); // The parameter payload looks like "{ param1 : value1, ..., paramN : valueN }", where each value can be primitive, complex, collection, entity, feed or collection. // Position the reader on the first node this.jsonLightParameterDeserializer.ReadPayloadStart( ODataPayloadKind.Parameter, this.duplicatePropertyNamesChecker, /*isReadingNestedPayload*/false, /*allowEmptyPayload*/true); return this.ReadAtStartImplementationSynchronously(); }
/// <summary> /// Reads the feed instance annotations for a top-level feed. /// </summary> /// <param name="feed">The <see cref="ODataFeed"/> to read the instance annotations for.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the top-level scope.</param> /// <param name="forFeedStart">true when parsing the instance annotations before the feed property; /// false when parsing the instance annotations after the feed property.</param> /// <param name="readAllFeedProperties">true if we should scan ahead for the annotations and ignore the actual data properties (used with /// the reordering reader); otherwise false.</param> internal void ReadTopLevelFeedAnnotations(ODataFeedBase feed, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool forFeedStart, bool readAllFeedProperties) { Debug.Assert(feed != null, "feed != null"); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.JsonReader.AssertNotBuffering(); bool buffering = false; try { while (this.JsonReader.NodeType == JsonNodeType.Property) { bool foundValueProperty = false; if (!forFeedStart && readAllFeedProperties) { // If this is not called for reading FeedStart and we already scanned ahead and processed all feed properties, we already checked for duplicate property names. // Use an empty duplicate property name checker since this.ParseProperty() read through the same property annotation of instance annotations again. duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(/*allowDuplicateProperties*/ true, this.JsonLightInputContext.ReadingResponse, !this.JsonLightInputContext.MessageReaderSettings.EnableFullValidation); } this.ProcessProperty( duplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParseResult, propertyName) => { switch (propertyParseResult) { case PropertyParsingResult.ODataInstanceAnnotation: case PropertyParsingResult.CustomInstanceAnnotation: // When we are reading the start of a feed (in scan-ahead mode or not) or when // we read the end of a feed and not in scan-ahead mode, read the value; // otherwise skip it. if (forFeedStart || !readAllFeedProperties) { this.ReadAndApplyFeedInstanceAnnotationValue(propertyName, feed, duplicatePropertyNamesChecker); } else { this.JsonReader.SkipValue(); } break; case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) == 0) { // We found the feed property and are done parsing property annotations; // When we are in the mode where we scan ahead and read all feed properties // (for the reordering scenario), we have to start buffering and continue // reading. Otherwise we found the feed's data property and are done. if (readAllFeedProperties) { this.JsonReader.StartBuffering(); buffering = true; this.JsonReader.SkipValue(); } else { foundValueProperty = true; } } else { throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_InvalidPropertyInTopLevelFeed(propertyName, JsonLightConstants.ODataValuePropertyName)); } break; case PropertyParsingResult.PropertyWithoutValue: // If we find a property without a value it means that we did not find the feed property (yet) // but an invalid property annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_InvalidPropertyAnnotationInTopLevelFeed(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: if (!(feed is ODataFeed)) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); } this.ReadMetadataReferencePropertyValue((ODataFeed)feed, propertyName); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntryAndFeedDeserializer_ReadTopLevelFeedAnnotations)); } }); if (foundValueProperty) { return; } } } finally { if (buffering) { Debug.Assert(readAllFeedProperties, "Expect the reader to be in buffering mode only when scanning to the end."); this.JsonReader.StopBuffering(); } } if (forFeedStart && !readAllFeedProperties) { // We did not find any properties or only instance annotations. throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_ExpectedFeedPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } }
/// <summary> /// Constructor. /// </summary> /// <param name="atomInputContext">The ATOM input context to read from.</param> internal ODataAtomCollectionDeserializer(ODataAtomInputContext atomInputContext) : base(atomInputContext) { this.duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); }
public void WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isTopLevel, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { this.WriteComplexVerifier.Should().NotBeNull("WriteComplexValue was called."); this.WriteComplexVerifier(complexValue, metadataTypeReference, isTopLevel, isOpenPropertyType, duplicatePropertyNamesChecker); }
private void RunPropertyParsingTest( string jsonInput, ODataJsonLightDeserializer.PropertyParsingResult expectedPropertyParsingResult, string expectedName, Action<JsonReader, DuplicatePropertyNamesChecker> additionalVerification = null, Func<JsonReader, string, object> readPropertyAnnotationValue = null, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false, true); } if (readPropertyAnnotationValue == null) { readPropertyAnnotationValue = (jsonReader, annotationName) => jsonReader.ReadPrimitiveValue(); } using (ODataJsonLightInputContext inputContext = this.CreateJsonLightInputContext(jsonInput)) { ODataJsonLightDeserializer deserializer = new ODataJsonLightPropertyAndValueDeserializer(inputContext); AdvanceReaderToFirstProperty(deserializer.JsonReader); deserializer.ProcessProperty( duplicatePropertyNamesChecker, (propertyName) => readPropertyAnnotationValue(deserializer.JsonReader, propertyName), (propertyParsingResult, propertyName) => { propertyParsingResult.Should().Be(expectedPropertyParsingResult, "parsing JSON object '{0}'", jsonInput); propertyName.Should().Be(expectedName, "reported name is wrong for JSON object '{0}'", jsonInput); if (additionalVerification != null) { additionalVerification(deserializer.JsonReader, duplicatePropertyNamesChecker); } }); } }
/// <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*/); }
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; }
/// <summary> /// Reads any next link annotation immediately after the end of a feed. /// </summary> /// <param name="feed">The feed being read.</param> /// <param name="expandedNavigationLinkInfo">The information about the expanded link. This must be non-null if we're reading an expanded feed, and must be null if we're reading a top-level feed.</param> /// <param name="duplicatePropertyNamesChecker">The top-level duplicate property names checker, if we're reading a top-level feed.</param> internal void ReadNextLinkAnnotationAtFeedEnd( ODataFeedBase feed, ODataJsonLightReaderNavigationLinkInfo expandedNavigationLinkInfo, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(feed != null, "feed != null"); // Check for annotations on the feed that occur after the feed itself. (Note: the only allowed one is odata.nextLink, and we fail for anything else.) // We do this slightly differently depending on whether the feed was an expanded navigation or a top-level feed. if (expandedNavigationLinkInfo != null) { this.ReadExpandedFeedAnnotationsAtFeedEnd(feed, expandedNavigationLinkInfo); } else { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); // Check for feed instance annotations that appear after the feed. bool isReordering = this.JsonReader is ReorderingJsonReader; this.ReadTopLevelFeedAnnotations(feed, duplicatePropertyNamesChecker, /*forFeedStart*/false, /*readAllFeedProperties*/isReordering); } }
public void WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isTopLevel, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(complexValue != null, "complexValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire complex instance; // for top-level complex properties we already wrote the object scope (and the context URI when needed). if (!isTopLevel) { this.JsonWriter.StartObjectScope(); } string typeName = complexValue.TypeName; if (isTopLevel) { Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties."); if (typeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnComplex); } } else { // In requests, we allow the property type reference to be null if the type name is specified in the OM if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel()) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForComplexValueRequest); } } // Resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata. IEdmComplexTypeReference complexValueTypeReference = (IEdmComplexTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType); Debug.Assert( metadataTypeReference == null || complexValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, complexValueTypeReference), "Complex property types must be the same as or inherit from the ones from metadata (unless open)."); typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, metadataTypeReference, complexValueTypeReference, isOpenPropertyType); if (typeName != null) { this.ODataAnnotationWriter.WriteODataTypeInstanceAnnotation(typeName); } // Write custom instance annotations this.InstanceAnnotationWriter.WriteInstanceAnnotations(complexValue.InstanceAnnotations); // Write the properties of the complex value as usual. Note we do not allow complex types to contain named stream properties. this.PropertySerializer.WriteProperties( complexValueTypeReference == null ? null : complexValueTypeReference.ComplexDefinition(), complexValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null /*projectedProperties */); // End the object scope which represents the complex instance; // for top-level complex properties we already wrote the end object scope. if (!isTopLevel) { this.JsonWriter.EndObjectScope(); } this.DecreaseRecursionDepth(); }
private void VerifyInvalidMetadataReferenceProperty(string propertyName) { string jsonInput = string.Format("{{\"" + JsonLightConstants.ODataPropertyAnnotationSeparatorChar + ODataAnnotationNames.ODataContext + "\":\"http://odata.org/$metadata\"," + "\"{0}\":42}}", propertyName); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false, true); using (ODataJsonLightInputContext inputContext = this.CreateJsonLightInputContext(jsonInput)) { ODataJsonLightEntryAndFeedDeserializer deserializer = new ODataJsonLightEntryAndFeedDeserializer(inputContext); deserializer.ReadPayloadStart(ODataPayloadKind.Unsupported, duplicatePropertyNamesChecker, false, false); Action readEntryContentAction = () => deserializer.ReadEntryContent(new TestJsonLightReaderEntryState()); readEntryContentAction .ShouldThrow<ODataException>("the property name \"{0}\" contains a hash but is not a valid URI or URI fragment", propertyName) .WithMessage(ErrorStrings.ValidationUtils_InvalidMetadataReferenceProperty(propertyName)); } }
private void AssertDuplicateMetadataReferencePropertyFails(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { string payload = "{\"#action\":42, \"#action\":43}"; using (ODataJsonLightInputContext inputContext = this.CreateJsonLightInputContext(payload)) { ODataJsonLightDeserializer deserializer = new ODataJsonLightPropertyAndValueDeserializer(inputContext); AdvanceReaderToFirstProperty(deserializer.JsonReader); deserializer.ProcessProperty( duplicatePropertyNamesChecker, (propertyName) => null, (propertyParsingResult, propertyName) => { propertyParsingResult.Should().Be(ODataJsonLightDeserializer.PropertyParsingResult.MetadataReferenceProperty, "parsing JSON object '{0}'", payload); propertyName.Should().Be("#action", "reported name is wrong for JSON object '{0}'", payload); deserializer.JsonReader.Should().BeOn(JsonNodeType.PrimitiveValue, 42); deserializer.JsonReader.Read(); deserializer.JsonReader.NodeType.Should().Be(JsonNodeType.Property); }); Action readDuplicateProperty = () => deserializer.ProcessProperty( duplicatePropertyNamesChecker, (propertyName) => null, (propertyParsingResult, propertyName) => { }); readDuplicateProperty.ShouldThrow<ODataException>("two metadata reference properties were encountered with the same name").WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicatePropertyNamesNotAllowed("#action")); } }
public void TwoMetadataReferencePropertiesShouldStillResultInDuplicationExceptionIfAllowingDuplicates() { DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(allowDuplicateProperties: true, isResponse: true); this.AssertDuplicateMetadataReferencePropertyFails(duplicatePropertyNamesChecker); }
public void ParsingDuplicateODataTypeAnnotationTargetingCustomInstanceAnnotationShouldFail() { var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => this.RunPropertyParsingTest("{\"[email protected]\":\"#typename\",\"[email protected]\":\"#typename\"}", ODataJsonLightDeserializer.PropertyParsingResult.CustomInstanceAnnotation, "custom.type", null, this.ReadODataTypePropertyAnnotation, duplicatePropertyNamesChecker); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationForInstanceAnnotationNotAllowed("odata.type", "custom.annotation")); }
public void ParsingDuplicateCustomInstanceAnnotationShouldFail2() { this.messageReaderSettings.ShouldIncludeAnnotation = ODataUtils.CreateAnnotationFilter("*"); var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => this.RunPropertyParsingTest("{\"@custom.type\":\"typename\",\"@custom.type\":\"typename\"}", ODataJsonLightDeserializer.PropertyParsingResult.CustomInstanceAnnotation, "custom.type", null, this.ReadODataTypePropertyAnnotation, duplicatePropertyNamesChecker); action(); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationNotAllowed("custom.type")); }
public void ParsingDuplicateODataInstanceAnnotationShouldFail() { var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => this.RunPropertyParsingTest("{\"@odata.deltaLink\":\"url\",\"@odata.deltaLink\":\"url\"}", ODataJsonLightDeserializer.PropertyParsingResult.ODataInstanceAnnotation, "odata.deltaLink", null, this.ReadODataTypePropertyAnnotation, duplicatePropertyNamesChecker); action(); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationNotAllowed("odata.deltaLink")); }
/// <summary> /// Reads instance annotation in the entry object. /// </summary> /// <param name="annotationName">The name of the instance annotation found.</param> /// <param name="anyPropertyFound">true if a non-annotation property has already been encountered.</param> /// <param name="typeAnnotationFound">true if the 'odata.type' annotation has already been encountered, or should have been by now.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the entry being read.</param> /// <returns>The value of the annotation.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.PrimitiveValue The value of the instance annotation property /// JsonNodeType.StartObject /// JsonNodeType.StartArray /// Post-Condition: JsonNodeType.EndObject The end of the entry object /// JsonNodeType.Property The next property after the instance annotation /// </remarks> internal object ReadEntryInstanceAnnotation(string annotationName, bool anyPropertyFound, bool typeAnnotationFound, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(!string.IsNullOrEmpty(annotationName), "!string.IsNullOrEmpty(annotationName)"); this.AssertJsonCondition(JsonNodeType.PrimitiveValue, JsonNodeType.StartObject, JsonNodeType.StartArray); switch (annotationName) { case ODataAnnotationNames.ODataType: // 'odata.type' if (!typeAnnotationFound) { return this.ReadODataTypeAnnotationValue(); } // We already read the odata.type if it was the first property in ReadEntryStart, so any other occurrence means // that it was not the first property. throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_EntryTypeAnnotationNotFirst); case ODataAnnotationNames.ODataId: // 'odata.id' if (anyPropertyFound) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_EntryInstanceAnnotationPrecededByProperty(annotationName)); } return this.ReadAnnotationStringValueAsUri(annotationName); case ODataAnnotationNames.ODataETag: // 'odata.etag' if (anyPropertyFound) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntryAndFeedDeserializer_EntryInstanceAnnotationPrecededByProperty(annotationName)); } return this.ReadAndValidateAnnotationStringValue(annotationName); case ODataAnnotationNames.ODataEditLink: // 'odata.editLink' case ODataAnnotationNames.ODataReadLink: // 'odata.readLink' case ODataAnnotationNames.ODataMediaEditLink: // 'odata.mediaEditLink' case ODataAnnotationNames.ODataMediaReadLink: // 'odata.mediaReadLink' return this.ReadAndValidateAnnotationStringValueAsUri(annotationName); case ODataAnnotationNames.ODataMediaContentType: // 'odata.mediaContentType' case ODataAnnotationNames.ODataMediaETag: // 'odata.mediaEtag' return this.ReadAndValidateAnnotationStringValue(annotationName); default: ODataAnnotationNames.ValidateIsCustomAnnotationName(annotationName); Debug.Assert( !this.MessageReaderSettings.ShouldSkipAnnotation(annotationName), "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(annotationName) -- otherwise we should have already skipped the custom annotation and won't see it here."); return this.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, annotationName); } }
/// <summary> /// Reads the value of a feed annotation (count or next link). /// </summary> /// <param name="annotationName">The name of the annotation found.</param> /// <param name="feed">The feed to read the annotation for; if non-null, the annotation value will be assigned to the feed.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker instance.</param> /// <remarks> /// Pre-Condition: JsonNodeType.PrimitiveValue The value of the annotation /// Post-Condition: JsonNodeType.EndObject The end of the feed object /// JsonNodeType.Property The next annotation after the current annotation /// </remarks> internal void ReadAndApplyFeedInstanceAnnotationValue(string annotationName, ODataFeedBase feed, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(!string.IsNullOrEmpty(annotationName), "!string.IsNullOrEmpty(annotationName)"); Debug.Assert(feed != null, "feed != null"); switch (annotationName) { case ODataAnnotationNames.ODataCount: feed.Count = this.ReadAndValidateAnnotationAsLongForIeee754Compatible(ODataAnnotationNames.ODataCount); break; case ODataAnnotationNames.ODataNextLink: feed.NextPageLink = this.ReadAndValidateAnnotationStringValueAsUri(ODataAnnotationNames.ODataNextLink); break; case ODataAnnotationNames.ODataDeltaLink: feed.DeltaLink = this.ReadAndValidateAnnotationStringValueAsUri(ODataAnnotationNames.ODataDeltaLink); break; default: ODataAnnotationNames.ValidateIsCustomAnnotationName(annotationName); Debug.Assert( !this.MessageReaderSettings.ShouldSkipAnnotation(annotationName), "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(annotationName) -- otherwise we should have already skipped the custom annotation and won't see it here."); object instanceAnnotationValue = this.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, annotationName); feed.InstanceAnnotations.Add(new ODataInstanceAnnotation(annotationName, instanceAnnotationValue.ToODataValue())); break; } }
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> /// 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 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 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> /// Read a complex value from the reader. /// </summary> /// <param name="complexTypeReference">The type reference of the value to read (or null if no type is available).</param> /// <param name="payloadTypeName">The name of the type specified in the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the complex value (possibly null).</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</param> /// <returns>The value read from the payload.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - the element to read the value for. /// XmlNodeType.Attribute - an attribute on the element to read the value for. /// Post-Condition: XmlNodeType.EndElement - the element has been read. /// /// Note that this method will not read null values, those should be handled by the caller already. /// </remarks> private ODataComplexValue ReadComplexValue( IEdmComplexTypeReference complexTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute); this.IncreaseRecursionDepth(); ODataComplexValue complexValue = new ODataComplexValue(); IEdmComplexType complexType = complexTypeReference == null ? null : (IEdmComplexType)complexTypeReference.Definition; // If we have a metadata type for the complex value, use that type name // otherwise use the type name from the payload (if there was any). complexValue.TypeName = complexType == null ? payloadTypeName : complexType.ODataFullName(); if (serializationTypeNameAnnotation != null) { complexValue.SetAnnotation(serializationTypeNameAnnotation); } // Move to the element (so that if we were on an attribute we can test the element for being empty) this.XmlReader.MoveToElement(); if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } else { duplicatePropertyNamesChecker.Clear(); } ReadOnlyEnumerable<ODataProperty> properties = new ReadOnlyEnumerable<ODataProperty>(); this.ReadPropertiesImplementation(complexType, properties, duplicatePropertyNamesChecker); complexValue.Properties = properties; this.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert(complexValue != null, "The method should never return null since it doesn't handle null values."); this.DecreaseRecursionDepth(); return complexValue; }
/// <summary> /// 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); } }
/// <summary> /// Constructor to create a new entry scope. /// </summary> /// <param name="state">The writer state of this scope.</param> /// <param name="entry">The entry for the new scope.</param> /// <param name="serializationInfo">The serialization info for the current entry.</param> /// <param name="navigationSource">The navigation source we are going to write entities for.</param> /// <param name="entityType">The entity type for the entries in the feed to be written (or null if the entity set base type should be used).</param> /// <param name="writerBehavior">The <see cref="ODataWriterBehavior"/> instance controlling the behavior of the writer.</param> /// <param name="selectedProperties">The selected properties of this scope.</param> /// <param name="odataUri">The ODataUri info of this scope.</param> protected DeltaEntryScope(WriterState state, ODataItem entry, ODataFeedAndEntrySerializationInfo serializationInfo, IEdmNavigationSource navigationSource, IEdmEntityType entityType, ODataWriterBehavior writerBehavior, SelectedPropertiesNode selectedProperties, ODataUri odataUri) : base(state, entry, navigationSource, entityType, selectedProperties, odataUri) { Debug.Assert(entry != null, "entry != null"); Debug.Assert( state == WriterState.DeltaEntry && entry is ODataEntry || state == WriterState.DeltaDeletedEntry && entry is ODataDeltaDeletedEntry, "entry must be either DeltaEntry or DeltaDeletedEntry."); Debug.Assert(writerBehavior != null, "writerBehavior != null"); this.duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(writerBehavior.AllowDuplicatePropertyNames, /*writingResponse*/ true); this.serializationInfo = serializationInfo; }