/// <summary> /// If <paramref name="annotationName"/> is under the odata namespace but is not known to ODataLib, move the JSON reader forward to skip the /// annotation name and value then return true; return false otherwise. /// </summary> /// <remarks> /// The unknown odata annotation is skipped so that when this version of the reader reads a resource set produced by a future version of ODataLib /// that contains an odata annotation that is not recognized on this version, we would simply ignore the annotation rather than failing. /// Note that when we add new odata annotations that cannot be skipped, we would bump the protocol version. /// </remarks> /// <param name="annotationName">The annotation name in question.</param> /// <param name="annotationValue">Outputs the .</param> /// <returns>Returns true if the annotation name and value is skipped; returns false otherwise.</returns> private bool SkippedOverUnknownODataAnnotation(string annotationName, out object annotationValue) { Debug.Assert(!string.IsNullOrEmpty(annotationName), "!string.IsNullOrEmpty(annotationName)"); this.AssertJsonCondition(JsonNodeType.Property); if (ODataAnnotationNames.IsUnknownODataAnnotationName(annotationName)) { annotationValue = ReadODataOrCustomInstanceAnnotationValue(annotationName); return(true); } annotationValue = null; return(false); }
/// <summary> /// Asynchronously read an entity reference link. /// </summary> /// <param name="propertyAndAnnotationCollector">The duplicate property names checker to check for duplicate properties and /// duplicate annotations; this is a separate instance per entity reference link.</param> /// <param name="topLevel">true if we are reading a singleton entity reference link at the top level; false if we are reading /// an entity reference link as part of a collection of entity reference links.</param> /// <returns> /// A task that represents the asynchronous read operation. /// The value of the TResult parameter contains an instance of <see cref="ODataEntityReferenceLink"/> which was read. /// </returns> /// <remarks> /// Pre-Condition: StartObject when the entity reference link is part of a collection /// Property the first property in the entity reference link (for a top-level link) /// EndObject the end object node of an entity reference link (for a top-level link) /// Post-Condition: EndInput for a top-level object /// EndArray for the last link in a collection of links /// Any for the first node of the next link in a collection of links /// </remarks> private async Task <ODataEntityReferenceLink> ReadSingleEntityReferenceLinkAsync(PropertyAndAnnotationCollector propertyAndAnnotationCollector, bool topLevel) { this.JsonReader.AssertNotBuffering(); if (!topLevel) { if (this.JsonReader.NodeType != JsonNodeType.StartObject) { // entity reference link has to be an object throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkMustBeObjectValue(this.JsonReader.NodeType)); } await this.JsonReader.ReadStartObjectAsync() .ConfigureAwait(false); } this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); ODataEntityReferenceLink[] entityReferenceLink = { null }; // Entity reference links use instance annotations. Fail if we find a property annotation. Func <string, Task <object> > propertyAnnotationValueReaderDelegate = annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLink(annotationName)); }; while (this.JsonReader.NodeType == JsonNodeType.Property) { this.ReadPropertyCustomAnnotationValueAsync = this.ReadCustomInstanceAnnotationValueAsync; await this.ProcessPropertyAsync( propertyAndAnnotationCollector, propertyAnnotationValueReaderDelegate, async (propertyParsingResult, propertyName) => { if (this.JsonReader.NodeType == JsonNodeType.Property) { // Read over property name await this.JsonReader.ReadAsync() .ConfigureAwait(false); } switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (!string.Equals(ODataAnnotationNames.ODataId, propertyName, StringComparison.Ordinal)) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyInEntityReferenceLink(propertyName, ODataAnnotationNames.ODataId)); } else if (entityReferenceLink[0] != null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MultipleUriPropertiesInEntityReferenceLink(ODataAnnotationNames.ODataId)); } // read the value of the 'odata.id' annotation string urlString = await this.JsonReader.ReadStringValueAsync(ODataAnnotationNames.ODataId) .ConfigureAwait(false); if (urlString == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkUrlCannotBeNull(ODataAnnotationNames.ODataId)); } entityReferenceLink[0] = new ODataEntityReferenceLink { Url = this.ProcessUriFromPayload(urlString) }; ReaderValidationUtils.ValidateEntityReferenceLink(entityReferenceLink[0]); break; case PropertyParsingResult.CustomInstanceAnnotation: if (entityReferenceLink[0] == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MissingEntityReferenceLinkProperty(ODataAnnotationNames.ODataId)); } Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); this.AssertJsonCondition(JsonNodeType.PrimitiveValue, JsonNodeType.StartObject, JsonNodeType.StartArray); ODataAnnotationNames.ValidateIsCustomAnnotationName(propertyName); Debug.Assert( !this.MessageReaderSettings.ShouldSkipAnnotation(propertyName), "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(propertyName) -- otherwise we should have already skipped the custom annotation and won't see it here."); object annotationValue = await this.ReadCustomInstanceAnnotationValueAsync(propertyAndAnnotationCollector, propertyName) .ConfigureAwait(false); entityReferenceLink[0].InstanceAnnotations.Add(new ODataInstanceAnnotation(propertyName, annotationValue.ToODataValue())); break; case PropertyParsingResult.PropertyWithValue: case PropertyParsingResult.PropertyWithoutValue: // entity reference link is denoted by odata.id annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidAnnotationInEntityReferenceLink(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadSingleEntityReferenceLink)); } }).ConfigureAwait(false); } if (entityReferenceLink[0] == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MissingEntityReferenceLinkProperty(ODataAnnotationNames.ODataId)); } // end of the entity reference link object await this.JsonReader.ReadEndObjectAsync() .ConfigureAwait(false); this.JsonReader.AssertNotBuffering(); return(entityReferenceLink[0]); }
/// <summary> /// Asynchronously reads the entity reference link instance annotations. /// </summary> /// <param name="links">The <see cref="ODataEntityReferenceLinks"/> to read the annotations for.</param> /// <param name="propertyAndAnnotationCollector">The duplicate property names checker for the entity reference links scope.</param> /// <param name="forLinksStart">true when parsing the instance annotations before the 'value' property; /// false when parsing the instance annotations after the 'value' property.</param> /// <returns>A task that represents the asynchronous read operation.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.Property The first property in the payload (or the first property after the context URI in responses) /// JsonNodeType.EndObject The end of the entity reference links object /// Post-Condition: JsonNodeType.EndObject When the end of the entity reference links object is reached /// Any The first node of the value of the 'url' property (if found) /// </remarks> private async Task ReadEntityReferenceLinksAnnotationsAsync( ODataEntityReferenceLinks links, PropertyAndAnnotationCollector propertyAndAnnotationCollector, bool forLinksStart) { Debug.Assert(links != null, "links != null"); Debug.Assert(propertyAndAnnotationCollector != null, "propertyAndAnnotationCollector != null"); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); this.JsonReader.AssertNotBuffering(); while (this.JsonReader.NodeType == JsonNodeType.Property) { // OData property annotations are not supported on entity reference links. Func <string, Task <object> > propertyAnnotationValueReaderDelegate = annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLinks); }; bool foundValueProperty = false; this.ReadPropertyCustomAnnotationValueAsync = this.ReadCustomInstanceAnnotationValueAsync; await this.ProcessPropertyAsync( propertyAndAnnotationCollector, propertyAnnotationValueReaderDelegate, async (propertyParseResult, propertyName) => { if (this.JsonReader.NodeType == JsonNodeType.Property) { // Read over property name await this.JsonReader.ReadAsync() .ConfigureAwait(false); } switch (propertyParseResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (string.Equals(ODataAnnotationNames.ODataNextLink, propertyName, StringComparison.Ordinal)) { this.AssertJsonCondition(JsonNodeType.PrimitiveValue); Uri nextPageUri = await this.ReadAndValidateAnnotationStringValueAsUriAsync(ODataAnnotationNames.ODataNextLink) .ConfigureAwait(false); Debug.Assert(links.NextPageLink == null, "We should have checked for duplicates already."); links.NextPageLink = nextPageUri; } else if (string.Equals(ODataAnnotationNames.ODataCount, propertyName, StringComparison.Ordinal)) { this.AssertJsonCondition(JsonNodeType.PrimitiveValue); Debug.Assert(!links.Count.HasValue, "We should have checked for duplicates already."); links.Count = await this.ReadAndValidateAnnotationAsLongForIeee754CompatibleAsync(ODataAnnotationNames.ODataCount) .ConfigureAwait(false); } else { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); } break; case PropertyParsingResult.CustomInstanceAnnotation: Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); this.AssertJsonCondition(JsonNodeType.PrimitiveValue, JsonNodeType.StartObject, JsonNodeType.StartArray); ODataAnnotationNames.ValidateIsCustomAnnotationName(propertyName); Debug.Assert( !this.MessageReaderSettings.ShouldSkipAnnotation(propertyName), "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(propertyName) -- otherwise we should have already skipped the custom annotation and won't see it here."); object annotationValue = await this.ReadCustomInstanceAnnotationValueAsync(propertyAndAnnotationCollector, propertyName) .ConfigureAwait(false); links.InstanceAnnotations.Add(new ODataInstanceAnnotation(propertyName, annotationValue.ToODataValue())); break; case PropertyParsingResult.PropertyWithValue: if (!string.Equals(JsonLightConstants.ODataValuePropertyName, propertyName, StringComparison.Ordinal)) { // We did not find a supported link collection property; fail. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidEntityReferenceLinksPropertyFound(propertyName, JsonLightConstants.ODataValuePropertyName)); } // We found the link collection property and are done parsing property annotations; foundValueProperty = true; break; case PropertyParsingResult.PropertyWithoutValue: // If we find a property without a value it means that we did not find the entity reference links property (yet) // but an invalid property annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyAnnotationInEntityReferenceLinks(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadEntityReferenceLinksAnnotations)); } }).ConfigureAwait(false); if (foundValueProperty) { return; } } if (forLinksStart) { // We did not find the 'value' property. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_ExpectedEntityReferenceLinksPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } this.AssertJsonCondition(JsonNodeType.EndObject); }
/// <summary> /// Reads the entity reference link instance annotations. /// </summary> /// <param name="links">The <see cref="ODataEntityReferenceLinks"/> to read the annotations for.</param> /// <param name="propertyAndAnnotationCollector">The duplicate property names checker for the entity reference links scope.</param> /// <param name="forLinksStart">true when parsing the instance annotations before the 'value' property; /// false when parsing the instance annotations after the 'value' property.</param> /// <remarks> /// Pre-Condition: JsonNodeType.Property The first property in the payload (or the first property after the context URI in responses) /// JsonNodeType.EndObject The end of the entity reference links object /// Post-Condition: JsonNodeType.EndObject When the end of the entity reference links object is reached /// Any The first node of the value of the 'url' property (if found) /// </remarks> private void ReadEntityReferenceLinksAnnotations(ODataEntityReferenceLinks links, PropertyAndAnnotationCollector propertyAndAnnotationCollector, bool forLinksStart) { Debug.Assert(links != null, "links != null"); Debug.Assert(propertyAndAnnotationCollector != null, "propertyAndAnnotationCollector != null"); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); this.JsonReader.AssertNotBuffering(); while (this.JsonReader.NodeType == JsonNodeType.Property) { // OData property annotations are not supported on entity reference links. Func <string, object> propertyAnnotationValueReader = annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLinks); }; bool foundValueProperty = false; this.ProcessProperty( propertyAndAnnotationCollector, propertyAnnotationValueReader, (propertyParseResult, propertyName) => { if (this.JsonReader.NodeType == JsonNodeType.Property) { // Read over property name this.JsonReader.Read(); } switch (propertyParseResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (string.CompareOrdinal(ODataAnnotationNames.ODataNextLink, propertyName) == 0) { this.ReadEntityReferenceLinksNextLinkAnnotationValue(links); } else if (string.CompareOrdinal(ODataAnnotationNames.ODataCount, propertyName) == 0) { this.ReadEntityReferenceCountAnnotationValue(links); } else { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); } break; case PropertyParsingResult.CustomInstanceAnnotation: Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); this.AssertJsonCondition(JsonNodeType.PrimitiveValue, JsonNodeType.StartObject, JsonNodeType.StartArray); ODataAnnotationNames.ValidateIsCustomAnnotationName(propertyName); Debug.Assert( !this.MessageReaderSettings.ShouldSkipAnnotation(propertyName), "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(propertyName) -- otherwise we should have already skipped the custom annotation and won't see it here."); object annotationValue = this.ReadCustomInstanceAnnotationValue(propertyAndAnnotationCollector, propertyName); links.InstanceAnnotations.Add(new ODataInstanceAnnotation(propertyName, annotationValue.ToODataValue())); break; case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0) { // We did not find a supported link collection property; fail. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidEntityReferenceLinksPropertyFound(propertyName, JsonLightConstants.ODataValuePropertyName)); } // We found the link collection property and are done parsing property annotations; foundValueProperty = true; break; case PropertyParsingResult.PropertyWithoutValue: // If we find a property without a value it means that we did not find the entity reference links property (yet) // but an invalid property annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyAnnotationInEntityReferenceLinks(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadEntityReferenceLinksAnnotations)); } }); if (foundValueProperty) { return; } } if (forLinksStart) { // We did not find the 'value' property. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_ExpectedEntityReferenceLinksPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } this.AssertJsonCondition(JsonNodeType.EndObject); }