/// <summary>
        /// Read an entity reference link.
        /// </summary>
        /// <param name="duplicatePropertyNamesChecker">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>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 ODataEntityReferenceLink ReadSingleEntityReferenceLink(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, 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));
                }

                this.JsonReader.ReadStartObject();
            }

            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);

            ODataEntityReferenceLink[] entityReferenceLink = { null };

            // Entity  reference links use instance annotations. Fail if we find a  property annotation.
            Func <string, object> propertyAnnotationValueReader =
                annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLink(annotationName)); };

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                this.ProcessProperty(
                    duplicatePropertyNamesChecker,
                    propertyAnnotationValueReader,
                    (propertyParsingResult, propertyName) =>
                {
                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        if (string.CompareOrdinal(ODataAnnotationNames.ODataId, propertyName) != 0)
                        {
                            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 = this.JsonReader.ReadStringValue(ODataAnnotationNames.ODataId);
                        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 = this.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, propertyName);
                        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));
                    }
                });
            }

            if (entityReferenceLink[0] == null)
            {
                throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MissingEntityReferenceLinkProperty(ODataAnnotationNames.ODataId));
            }

            // end of the entity reference link object
            this.JsonReader.ReadEndObject();

            this.JsonReader.AssertNotBuffering();
            return(entityReferenceLink[0]);
        }
        /// <summary>
        /// Reads the entity reference link instance annotations.
        /// </summary>
        /// <param name="links">The <see cref="ODataEntityReferenceLinks"/> to read the annotations for.</param>
        /// <param name="duplicatePropertyNamesChecker">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, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool forLinksStart)
        {
            Debug.Assert(links != null, "links != null");
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != 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(
                    duplicatePropertyNamesChecker,
                    propertyAnnotationValueReader,
                    (propertyParseResult, propertyName) =>
                {
                    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(duplicatePropertyNamesChecker, 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);
        }