/// <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 })); }
/// <summary> /// Initializes a new instance of the <see cref="ODataJsonLightContextUriParseResult"/> class. /// </summary> /// <param name="model">The model to use when resolving the target of the URI.</param> /// <param name="contextUriFromPayload">The context URI read from the payload.</param> private ODataJsonLightContextUriParser(IEdmModel model, Uri contextUriFromPayload) { Debug.Assert(model != null, "model != null"); if (!model.IsUserModel()) { throw new ODataException(ODataErrorStrings.ODataJsonLightContextUriParser_NoModel); } this.model = model; this.parseResult = new ODataJsonLightContextUriParseResult(contextUriFromPayload); }
/// <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> /// <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 void ReadPayloadStart( 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."); string contextUriAnnotationValue = this.ReadPayloadStartImplementation( payloadKind, duplicatePropertyNamesChecker, isReadingNestedPayload, allowEmptyPayload); ODataJsonLightContextUriParseResult parseResult = null; // 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) { parseResult = this.jsonLightInputContext.PayloadKindDetectionState == null ? null : this.jsonLightInputContext.PayloadKindDetectionState.ContextUriParseResult; if (parseResult == null && contextUriAnnotationValue != null) { parseResult = ODataJsonLightContextUriParser.Parse( this.Model, contextUriAnnotationValue, payloadKind, this.Version, this.MessageReaderSettings.ReaderBehavior, this.JsonLightInputContext.ReadingResponse); } } this.contextUriParseResult = parseResult; #if DEBUG this.contextUriParseResultReady = true; #endif }
/// <summary> /// Validates that the parsed context URI from the payload is consistent with the expected /// collection item type when reading collection payloads. /// </summary> /// <param name="contextUriParseResult">The parse result of the context URI from the payload.</param> /// <param name="expectedItemTypeReference">The expected item type of the collection items.</param> /// <returns>The actual item type of the collection items.</returns> internal static IEdmTypeReference ValidateCollectionContextUriAndGetPayloadItemTypeReference( ODataJsonLightContextUriParseResult contextUriParseResult, IEdmTypeReference expectedItemTypeReference) { if (contextUriParseResult == null || contextUriParseResult.EdmType == null) { return expectedItemTypeReference; } Debug.Assert(contextUriParseResult.EdmType is IEdmCollectionType, "contextUriParseResult.EdmType is IEdmCollectionType"); IEdmCollectionType actualCollectionType = (IEdmCollectionType)contextUriParseResult.EdmType; if (expectedItemTypeReference != null) { // We allow co-variance in collection types (e.g., expecting the item type of Geography from a payload of Collection(GeographyPoint). if (!expectedItemTypeReference.IsAssignableFrom(actualCollectionType.ElementType)) { throw new ODataException(OData.Core.Strings.ReaderValidationUtils_ContextUriDoesNotReferTypeAssignableToExpectedType( UriUtils.UriToString(contextUriParseResult.ContextUri), actualCollectionType.ElementType.ODataFullName(), expectedItemTypeReference.ODataFullName())); } } return actualCollectionType.ElementType; }
/// <summary> /// Validates that the parsed context URI from the payload is consistent with the expected /// entity set and entity type when reading a feed or entry payload. /// </summary> /// <param name="contextUriParseResult">The parse result of the context URI from the payload.</param> /// <param name="scope">The top-level scope representing the reader state.</param> /// <param name="updateScope">Whether to update scope when validating.</param> internal static void ValidateFeedOrEntryContextUri(ODataJsonLightContextUriParseResult contextUriParseResult, ODataReaderCore.Scope scope, bool updateScope) { if (contextUriParseResult.EdmType is IEdmCollectionType) { ValidateFeedContextUri(contextUriParseResult, scope, updateScope); return; } Debug.Assert(contextUriParseResult != null, "contextUriParseResult != null"); Debug.Assert( contextUriParseResult.NavigationSource != null || contextUriParseResult.EdmType != null, "contextUriParseResult.EntitySet != null || contextUriParseResult.EdmType != null"); Debug.Assert(contextUriParseResult.EdmType is IEdmEntityType, "contextUriParseResult.StructuredType is IEdmEntityType"); // Set the navigation source name or make sure the navigation source names match. if (scope.NavigationSource == null) { if (updateScope) { scope.NavigationSource = contextUriParseResult.NavigationSource; } } else if (contextUriParseResult.NavigationSource != null && string.CompareOrdinal(scope.NavigationSource.FullNavigationSourceName(), contextUriParseResult.NavigationSource.FullNavigationSourceName()) != 0) { throw new ODataException(Strings.ReaderValidationUtils_ContextUriValidationInvalidExpectedEntitySet( UriUtils.UriToString(contextUriParseResult.ContextUri), contextUriParseResult.NavigationSource.FullNavigationSourceName(), scope.NavigationSource.FullNavigationSourceName())); } // Set the entity type or make sure the entity types are assignable. IEdmEntityType payloadEntityType = (IEdmEntityType)contextUriParseResult.EdmType; if (scope.EntityType == null) { if (updateScope) { scope.EntityType = payloadEntityType; } } else if (scope.EntityType.IsAssignableFrom(payloadEntityType)) { if (updateScope) { scope.EntityType = payloadEntityType; } } else if (!payloadEntityType.IsAssignableFrom(scope.EntityType)) { throw new ODataException(Strings.ReaderValidationUtils_ContextUriValidationInvalidExpectedEntityType( UriUtils.UriToString(contextUriParseResult.ContextUri), contextUriParseResult.EdmType.ODataFullName(), scope.EntityType.FullName())); } }
/// <summary> /// The validate feed context uri. /// </summary> /// <param name="contextUriParseResult"> /// The context uri parse result. /// </param> /// <param name="scope"> /// The scope. /// </param> /// <param name="updateScope"> /// The update scope. /// </param> private static void ValidateFeedContextUri(ODataJsonLightContextUriParseResult contextUriParseResult, ODataReaderCore.Scope scope, bool updateScope) { // TODO: add validation logic for a feed context uri }
/// <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 }); }
private void CompareContextUriParseResults(ODataJsonLightContextUriParseResult expected, ODataJsonLightContextUriParseResult actual, string expectedPathStr) { this.Assert.AreEqual(expected.MetadataDocumentUri, actual.MetadataDocumentUri, "Metadata document URIs don't match."); this.Assert.AreEqual(expected.NavigationSource, actual.NavigationSource, "Entity sets don't match."); if (expected.EdmType is IEdmCollectionType) { this.Assert.AreEqual(((IEdmCollectionType)expected.EdmType).ElementType.Definition, ((IEdmCollectionType)actual.EdmType).ElementType.Definition, "Entity types don't match."); } else { this.Assert.AreEqual(expected.EdmType, actual.EdmType, "Entity types don't match."); } this.Assert.AreEqual(expected.SelectQueryOption, actual.SelectQueryOption, "Select query option properties don't match."); if (expectedPathStr != null) { this.Assert.IsNotNull(actual.Path, "Path should not be null"); this.Assert.AreEqual(expectedPathStr, actual.Path.ToContextUrlPathString(), "Path not equal"); } this.Assert.AreEqual(expected.DeltaKind, actual.DeltaKind, "Delta kind doesn't match."); }