/// <summary>
        /// Writes a element (EntitySet, Singleton or FunctionImport) in service document.
        /// </summary>
        /// <param name="serviceDocumentElement">The element in service document to write.</param>
        /// <param name="kind">Kind of the service document element, optional for entityset's must for FunctionImport and Singleton.</param>
        private void WriteServiceDocumentElement(ODataServiceDocumentElement serviceDocumentElement, string kind)
        {
            // validate that the resource has a non-null url.
            ValidationUtils.ValidateServiceDocumentElement(serviceDocumentElement, ODataFormat.Json);

            // "{"
            this.JsonWriter.StartObjectScope();

            // "name": ...
            this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementName);
            this.JsonWriter.WriteValue(serviceDocumentElement.Name);

            // Do not write title if it is null or empty, or if title is the same as name.
            if (!string.IsNullOrEmpty(serviceDocumentElement.Title) && !serviceDocumentElement.Title.Equals(serviceDocumentElement.Name, StringComparison.Ordinal))
            {
                // "title": ...
                this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementTitle);
                this.JsonWriter.WriteValue(serviceDocumentElement.Title);
            }

            // Not always writing because it can be null if an ODataEntitySetInfo, not necessary to write this. Required for the others though.
            if (kind != null)
            {
                // "kind": ...
                this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementKind);
                this.JsonWriter.WriteValue(kind);
            }

            // "url": ...
            this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementUrlName);
            this.JsonWriter.WriteValue(this.UriToString(serviceDocumentElement.Url));

            // "}"
            this.JsonWriter.EndObjectScope();
        }
        /// <summary>
        /// Creates a <see cref="ODataServiceDocumentElement"/>.
        /// </summary>
        /// <param name="kind">Property values for the "kind" property.</param>
        /// <returns>A <see cref="ODataServiceDocumentElement"/> instance.</returns>
        private static ODataServiceDocumentElement CreateServiceDocumentElement(string[] kind)
        {
            // If not specified its an entity set.
            if (kind[0] == null)
            {
                return(new ODataEntitySetInfo());
            }

            ODataServiceDocumentElement serviceDocumentElement = null;

            if (kind[0].Equals(JsonLightConstants.ServiceDocumentEntitySetKindName, StringComparison.Ordinal))
            {
                serviceDocumentElement = new ODataEntitySetInfo();
            }
            else if (kind[0].Equals(JsonLightConstants.ServiceDocumentFunctionImportKindName, StringComparison.Ordinal))
            {
                serviceDocumentElement = new ODataFunctionImportInfo();
            }
            else if (kind[0].Equals(JsonLightConstants.ServiceDocumentSingletonKindName, StringComparison.Ordinal))
            {
                serviceDocumentElement = new ODataSingletonInfo();
            }

            return(serviceDocumentElement);
        }
        /// <summary>
        /// Writes a service document element in service document.
        /// </summary>
        /// <param name="serviceDocumentElement">The serviceDocument element resource to write.</param>
        /// <param name="elementName">The element name of the service document element to write.</param>
        private void WriteNonEntitySetInfoElement(ODataServiceDocumentElement serviceDocumentElement, string elementName)
        {
            // validate that the resource has a non-null url.
#pragma warning disable 618
            ValidationUtils.ValidateServiceDocumentElement(serviceDocumentElement, ODataFormat.Atom);
#pragma warning restore 618

            // <metadata:elementName>
            this.XmlWriter.WriteStartElement(AtomConstants.ODataMetadataNamespacePrefix, elementName, AtomConstants.ODataMetadataNamespace);

            // The name of the elementName is the resource name; The href of the <m:elementName> element must be the link for the elementName.
            // Since we model the collection as having a 'Name' (for JSON) we require a base Uri for Atom/Xml.
            this.XmlWriter.WriteAttributeString(AtomConstants.AtomHRefAttributeName, this.UriToUrlAttributeValue(serviceDocumentElement.Url));

            // TODO: According to the V4 spec we might want to omit writing this if the Url is the same as the Name, writing it always for now.
            this.atomServiceDocumentMetadataSerializer.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, serviceDocumentElement.Name);

            // </metadata:elementName>
            this.XmlWriter.WriteEndElement();
        }
        /// <summary>
        /// Reads a resource collection within a service document.
        /// </summary>
        /// <param name="propertyAndAnnotationCollector">The <see cref="PropertyAndAnnotationCollector"/> to use for parsing annotations within the service document element object.</param>
        /// <returns>A <see cref="ODataEntitySetInfo"/> representing the read resource collection.</returns>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.StartObject:     The beginning of the JSON object representing the service document element.
        ///                 other:                        Will throw with an appropriate message on any other node type encountered.
        /// Post-Condition: JsonNodeType.StartObject:     The beginning of the next resource collection in the array.
        ///                 JsonNodeType.EndArray:        The end of the array.
        ///                 other:                        Any other node type occuring after the end object of the current service document element. (Would be invalid).
        /// </remarks>
        private ODataServiceDocumentElement ReadServiceDocumentElement(PropertyAndAnnotationCollector propertyAndAnnotationCollector)
        {
            this.JsonReader.ReadStartObject();
            string[] name  = { null };
            string[] url   = { null };
            string[] kind  = { null };
            string[] title = { null };

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                // OData property annotations are not supported in service document element objects.
                Func <string, object> propertyAnnotationValueReader = annotationName => { throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_PropertyAnnotationInServiceDocumentElement(annotationName)); };

                this.ProcessProperty(
                    propertyAndAnnotationCollector,
                    propertyAnnotationValueReader,
                    (propertyParsingResult, propertyName) =>
                {
                    if (this.JsonReader.NodeType == JsonNodeType.Property)
                    {
                        // Read over property name
                        this.JsonReader.Read();
                    }

                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_InstanceAnnotationInServiceDocumentElement(propertyName));

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        this.JsonReader.SkipValue();
                        break;

                    case PropertyParsingResult.PropertyWithoutValue:
                        throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_PropertyAnnotationWithoutProperty(propertyName));

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(Strings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));

                    case PropertyParsingResult.PropertyWithValue:
                        if (string.CompareOrdinal(JsonLightConstants.ODataServiceDocumentElementName, propertyName) == 0)
                        {
                            if (name[0] != null)
                            {
                                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_DuplicatePropertiesInServiceDocumentElement(JsonLightConstants.ODataServiceDocumentElementName));
                            }

                            name[0] = this.JsonReader.ReadStringValue();
                        }
                        else if (string.CompareOrdinal(JsonLightConstants.ODataServiceDocumentElementUrlName, propertyName) == 0)
                        {
                            if (url[0] != null)
                            {
                                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_DuplicatePropertiesInServiceDocumentElement(JsonLightConstants.ODataServiceDocumentElementUrlName));
                            }

                            url[0] = this.JsonReader.ReadStringValue();
                            ValidationUtils.ValidateServiceDocumentElementUrl(url[0]);
                        }
                        else if (string.CompareOrdinal(JsonLightConstants.ODataServiceDocumentElementKind, propertyName) == 0)
                        {
                            if (kind[0] != null)
                            {
                                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_DuplicatePropertiesInServiceDocumentElement(JsonLightConstants.ODataServiceDocumentElementKind));
                            }

                            kind[0] = this.JsonReader.ReadStringValue();
                        }
                        else if (string.CompareOrdinal(JsonLightConstants.ODataServiceDocumentElementTitle, propertyName) == 0)
                        {
                            if (title[0] != null)
                            {
                                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_DuplicatePropertiesInServiceDocumentElement(JsonLightConstants.ODataServiceDocumentElementTitle));
                            }

                            title[0] = this.JsonReader.ReadStringValue();
                        }
                        else
                        {
                            throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_UnexpectedPropertyInServiceDocumentElement(propertyName, JsonLightConstants.ODataServiceDocumentElementName, JsonLightConstants.ODataServiceDocumentElementUrlName));
                        }

                        break;
                    }
                });
            }

            // URL and Name are mandatory
            if (string.IsNullOrEmpty(name[0]))
            {
                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_MissingRequiredPropertyInServiceDocumentElement(JsonLightConstants.ODataServiceDocumentElementName));
            }

            if (string.IsNullOrEmpty(url[0]))
            {
                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_MissingRequiredPropertyInServiceDocumentElement(JsonLightConstants.ODataServiceDocumentElementUrlName));
            }

            ODataServiceDocumentElement serviceDocumentElement = null;

            if (kind[0] != null)
            {
                if (kind[0].Equals(JsonLightConstants.ServiceDocumentEntitySetKindName, StringComparison.Ordinal))
                {
                    serviceDocumentElement = new ODataEntitySetInfo();
                }
                else if (kind[0].Equals(JsonLightConstants.ServiceDocumentFunctionImportKindName, StringComparison.Ordinal))
                {
                    serviceDocumentElement = new ODataFunctionImportInfo();
                }
                else if (kind[0].Equals(JsonLightConstants.ServiceDocumentSingletonKindName, StringComparison.Ordinal))
                {
                    serviceDocumentElement = new ODataSingletonInfo();
                }
            }
            else
            {
                // if not specified its an entity set.
                serviceDocumentElement = new ODataEntitySetInfo();
            }

            if (serviceDocumentElement != null)
            {
                serviceDocumentElement.Url   = this.ProcessUriFromPayload(url[0]);
                serviceDocumentElement.Name  = name[0];
                serviceDocumentElement.Title = title[0];
            }

            this.JsonReader.ReadEndObject();

            return(serviceDocumentElement);
        }
        /// <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>
        /// <param name="propertyAndAnnotationCollector">The duplicate property names checker to use for the top-level scope.</param>
        /// <returns>An <see cref="ODataServiceDocument"/> representing the read service document.</returns>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.Property   The property right after the context URI property.
        ///                 JsonNodeType.EndObject  The EndObject of the service document.
        /// Post-Condition: Any                     The node after the EndObject of the service document.
        /// </remarks>
        private ODataServiceDocument ReadServiceDocumentImplementation(PropertyAndAnnotationCollector propertyAndAnnotationCollector)
        {
            Debug.Assert(propertyAndAnnotationCollector != null, "propertyAndAnnotationCollector != null");
            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);

            List <ODataServiceDocumentElement>[] serviceDocumentElements = { null };

            // Read all the properties in the service document object; we ignore all except 'value'.
            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                // Property annotations are not allowed on the 'value' property, so fail if we see one.
                Func <string, object> readPropertyAnnotationInServiceDoc = annotationName => { throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_PropertyAnnotationInServiceDocument(annotationName, JsonLightConstants.ODataValuePropertyName)); };

                this.ProcessProperty(
                    propertyAndAnnotationCollector,
                    readPropertyAnnotationInServiceDoc,
                    (propertyParsingResult, propertyName) =>
                {
                    if (this.JsonReader.NodeType == JsonNodeType.Property)
                    {
                        // Read over property name
                        this.JsonReader.Read();
                    }

                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_InstanceAnnotationInServiceDocument(propertyName, JsonLightConstants.ODataValuePropertyName));

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        this.JsonReader.SkipValue();
                        break;

                    case PropertyParsingResult.PropertyWithoutValue:
                        throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_PropertyAnnotationWithoutProperty(propertyName));

                    case PropertyParsingResult.PropertyWithValue:
                        if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) == 0)
                        {
                            // Fail if we've already processed a 'value' property.
                            if (serviceDocumentElements[0] != null)
                            {
                                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_DuplicatePropertiesInServiceDocument(JsonLightConstants.ODataValuePropertyName));
                            }

                            serviceDocumentElements[0] = new List <ODataServiceDocumentElement>();

                            // Read the value of the 'value' property.
                            this.JsonReader.ReadStartArray();
                            PropertyAndAnnotationCollector resourceCollectionPropertyAndAnnotationCollector = this.CreatePropertyAndAnnotationCollector();

                            while (this.JsonReader.NodeType != JsonNodeType.EndArray)
                            {
                                ODataServiceDocumentElement serviceDocumentElement = this.ReadServiceDocumentElement(resourceCollectionPropertyAndAnnotationCollector);

                                if (serviceDocumentElement != null)
                                {
                                    serviceDocumentElements[0].Add(serviceDocumentElement);
                                }

                                resourceCollectionPropertyAndAnnotationCollector.Reset();
                            }

                            this.JsonReader.ReadEndArray();
                        }
                        else
                        {
                            throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_UnexpectedPropertyInServiceDocument(propertyName, JsonLightConstants.ODataValuePropertyName));
                        }

                        break;

                    case PropertyParsingResult.EndOfObject:
                        break;

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(Strings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));
                    }
                });
            }

            if (serviceDocumentElements[0] == null)
            {
                throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_MissingValuePropertyInServiceDocument(JsonLightConstants.ODataValuePropertyName));
            }

            // Read over the end object (nothing else can happen after all properties have been read)
            this.JsonReader.ReadEndObject();

            return(new ODataServiceDocument
            {
                EntitySets = new ReadOnlyEnumerable <ODataEntitySetInfo>(serviceDocumentElements[0].OfType <ODataEntitySetInfo>().ToList()),
                FunctionImports = new ReadOnlyEnumerable <ODataFunctionImportInfo>(serviceDocumentElements[0].OfType <ODataFunctionImportInfo>().ToList()),
                Singletons = new ReadOnlyEnumerable <ODataSingletonInfo>(serviceDocumentElements[0].OfType <ODataSingletonInfo>().ToList())
            });
        }
        /// <summary>
        /// Reads an atom:title element and adds the new information to <paramref name="odataServiceDocumentElement"/> and (if ATOM metadata reading is on) <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The collection metadata object to augment, or null if metadata reading is not on.</param>
        /// <param name="odataServiceDocumentElement">The non-null service document element info object being populated.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the title element.
        /// Post-Condition: Any                 - The next node after the title element.
        /// </remarks>
        internal void ReadTitleElementInCollection(AtomResourceCollectionMetadata collectionMetadata, ODataServiceDocumentElement odataServiceDocumentElement)
        {
            Debug.Assert(!this.ReadAtomMetadata || collectionMetadata != null, "collectionMetadata parameter should be non-null when ATOM metadata reading is enabled.");
            Debug.Assert(odataServiceDocumentElement != null, "collectionInfo != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomTitleElementName, "Expected element named 'title'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Element 'title' should be in the atom namespace.");

            AtomTextConstruct titleTextConstruct = this.ReadTitleElement();

            if (odataServiceDocumentElement.Name == null)
            {
                odataServiceDocumentElement.Name = titleTextConstruct.Text;
            }

            if (this.ReadAtomMetadata)
            {
                collectionMetadata.Title = titleTextConstruct;
            }
        }
        /// <summary>
        /// Writes a element (EntitySet, Singleton or FunctionImport) in service document.
        /// </summary>
        /// <param name="serviceDocumentElement">The element in service document to write.</param>
        /// <param name="kind">Kind of the service document element, optional for entityset's must for FunctionImport and Singleton.</param>
        private void WriteServiceDocumentElement(ODataServiceDocumentElement serviceDocumentElement, string kind)
        {
            // validate that the resource has a non-null url.
            this.WriterValidator.ValidateServiceDocumentElement(serviceDocumentElement, ODataFormat.Json);

            // "{"
            this.JsonWriter.StartObjectScope();

            // "name": ...
            this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementName);
            this.JsonWriter.WriteValue(serviceDocumentElement.Name);

            // Do not write title if it is null or empty, or if title is the same as name.
            if (!string.IsNullOrEmpty(serviceDocumentElement.Title) && !serviceDocumentElement.Title.Equals(serviceDocumentElement.Name, StringComparison.Ordinal))
            {
                // "title": ...
                this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementTitle);
                this.JsonWriter.WriteValue(serviceDocumentElement.Title);
            }

            // Not always writing because it can be null if an ODataEntitySetInfo, not necessary to write this. Required for the others though.
            if (kind != null)
            {
                // "kind": ...
                this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementKind);
                this.JsonWriter.WriteValue(kind);
            }

            // "url": ...
            this.JsonWriter.WriteName(JsonLightConstants.ODataServiceDocumentElementUrlName);
            this.JsonWriter.WriteValue(this.UriToString(serviceDocumentElement.Url));

            // "}"
            this.JsonWriter.EndObjectScope();
        }
        /// <summary>
        /// Asynchronously reads a resource collection within a service document.
        /// </summary>
        /// <param name="propertyAndAnnotationCollector">
        /// The <see cref="PropertyAndAnnotationCollector"/> to use for parsing annotations within the service document element object.
        /// </param>
        /// <returns>
        /// A task that represents the asynchronous read operation.
        /// The value of the TResult parameter contains an <see cref="ODataEntitySetInfo"/> representing the read resource collection.
        /// </returns>
        /// <remarks>
        /// Pre-Condition:  JsonNodeType.StartObject:     The beginning of the JSON object representing the service document element.
        ///                 other:                        Will throw with an appropriate message on any other node type encountered.
        /// Post-Condition: JsonNodeType.StartObject:     The beginning of the next resource collection in the array.
        ///                 JsonNodeType.EndArray:        The end of the array.
        ///                 other:                        Any other node type occuring after the end object of the current service document element. (Would be invalid).
        /// </remarks>
        private async Task <ODataServiceDocumentElement> ReadServiceDocumentElementAsync(PropertyAndAnnotationCollector propertyAndAnnotationCollector)
        {
            await this.JsonReader.ReadStartObjectAsync()
            .ConfigureAwait(false);

            string[] name  = { null };
            string[] url   = { null };
            string[] kind  = { null };
            string[] title = { null };

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                // OData property annotations are not supported in service document element objects.
                Func <string, Task <object> > propertyAnnotationValueReaderAsync = annotationName =>
                {
                    throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_PropertyAnnotationInServiceDocumentElement(annotationName));
                };

                await this.ProcessPropertyAsync(
                    propertyAndAnnotationCollector,
                    propertyAnnotationValueReaderAsync,
                    async (propertyParsingResult, propertyName) =>
                {
                    if (this.JsonReader.NodeType == JsonNodeType.Property)
                    {
                        // Read over property name
                        await this.JsonReader.ReadAsync()
                        .ConfigureAwait(false);
                    }

                    switch (propertyParsingResult)
                    {
                    case PropertyParsingResult.ODataInstanceAnnotation:
                        throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_InstanceAnnotationInServiceDocumentElement(propertyName));

                    case PropertyParsingResult.CustomInstanceAnnotation:
                        await this.JsonReader.SkipValueAsync()
                        .ConfigureAwait(false);
                        break;

                    case PropertyParsingResult.PropertyWithoutValue:
                        throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_PropertyAnnotationWithoutProperty(propertyName));

                    case PropertyParsingResult.MetadataReferenceProperty:
                        throw new ODataException(Strings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName));

                    case PropertyParsingResult.PropertyWithValue:
                        if (string.Equals(JsonLightConstants.ODataServiceDocumentElementName, propertyName, StringComparison.Ordinal))
                        {
                            ValidateServiceDocumentElementHasNoRepeatedProperty(JsonLightConstants.ODataServiceDocumentElementName, name);

                            name[0] = await this.JsonReader.ReadStringValueAsync()
                                      .ConfigureAwait(false);
                        }
                        else if (string.Equals(JsonLightConstants.ODataServiceDocumentElementUrlName, propertyName, StringComparison.Ordinal))
                        {
                            ValidateServiceDocumentElementHasNoRepeatedProperty(JsonLightConstants.ODataServiceDocumentElementUrlName, url);

                            url[0] = await this.JsonReader.ReadStringValueAsync()
                                     .ConfigureAwait(false);
                            ValidationUtils.ValidateServiceDocumentElementUrl(url[0]);
                        }
                        else if (string.Equals(JsonLightConstants.ODataServiceDocumentElementKind, propertyName, StringComparison.Ordinal))
                        {
                            ValidateServiceDocumentElementHasNoRepeatedProperty(JsonLightConstants.ODataServiceDocumentElementKind, kind);

                            kind[0] = await this.JsonReader.ReadStringValueAsync()
                                      .ConfigureAwait(false);
                        }
                        else if (string.Equals(JsonLightConstants.ODataServiceDocumentElementTitle, propertyName, StringComparison.Ordinal))
                        {
                            ValidateServiceDocumentElementHasNoRepeatedProperty(JsonLightConstants.ODataServiceDocumentElementTitle, title);

                            title[0] = await this.JsonReader.ReadStringValueAsync()
                                       .ConfigureAwait(false);
                        }
                        else
                        {
                            throw new ODataException(Strings.ODataJsonLightServiceDocumentDeserializer_UnexpectedPropertyInServiceDocumentElement(
                                                         propertyName,
                                                         JsonLightConstants.ODataServiceDocumentElementName,
                                                         JsonLightConstants.ODataServiceDocumentElementUrlName));
                        }

                        break;
                    }
                }).ConfigureAwait(false);
            }

            // URL and Name are mandatory
            ValidateServiceDocumentElementHasRequiredProperty(JsonLightConstants.ODataServiceDocumentElementName, name);
            ValidateServiceDocumentElementHasRequiredProperty(JsonLightConstants.ODataServiceDocumentElementUrlName, url);

            ODataServiceDocumentElement serviceDocumentElement = CreateServiceDocumentElement(kind);

            if (serviceDocumentElement != null)
            {
                serviceDocumentElement.Url   = this.ProcessUriFromPayload(url[0]);
                serviceDocumentElement.Name  = name[0];
                serviceDocumentElement.Title = title[0];
            }

            await this.JsonReader.ReadEndObjectAsync()
            .ConfigureAwait(false);

            return(serviceDocumentElement);
        }
        /// <summary>
        /// Writes a service document element in service document.
        /// </summary>
        /// <param name="serviceDocumentElement">The serviceDocument element resource to write.</param>
        /// <param name="elementName">The element name of the service document element to write.</param>
        private void WriteNonEntitySetInfoElement(ODataServiceDocumentElement serviceDocumentElement, string elementName)
        {
            // validate that the resource has a non-null url.
#pragma warning disable 618
            ValidationUtils.ValidateServiceDocumentElement(serviceDocumentElement, ODataFormat.Atom);
#pragma warning restore 618

            // <metadata:elementName>
            this.XmlWriter.WriteStartElement(AtomConstants.ODataMetadataNamespacePrefix, elementName, AtomConstants.ODataMetadataNamespace);

            // The name of the elementName is the resource name; The href of the <m:elementName> element must be the link for the elementName.
            // Since we model the collection as having a 'Name' (for JSON) we require a base Uri for Atom/Xml.
            this.XmlWriter.WriteAttributeString(AtomConstants.AtomHRefAttributeName, this.UriToUrlAttributeValue(serviceDocumentElement.Url));

            // TODO: According to the V4 spec we might want to omit writing this if the Url is the same as the Name, writing it always for now.
            this.atomServiceDocumentMetadataSerializer.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, serviceDocumentElement.Name);

            // </metadata:elementName>
            this.XmlWriter.WriteEndElement();
        }
        /// <summary>
        /// Reads an atom:title element and adds the new information to <paramref name="odataServiceDocumentElement"/> and (if ATOM metadata reading is on) <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The collection metadata object to augment, or null if metadata reading is not on.</param>
        /// <param name="odataServiceDocumentElement">The non-null service document element info object being populated.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the title element.
        /// Post-Condition: Any                 - The next node after the title element. 
        /// </remarks>
        internal void ReadTitleElementInCollection(AtomResourceCollectionMetadata collectionMetadata, ODataServiceDocumentElement odataServiceDocumentElement)
        {
            Debug.Assert(!this.ReadAtomMetadata || collectionMetadata != null, "collectionMetadata parameter should be non-null when ATOM metadata reading is enabled.");
            Debug.Assert(odataServiceDocumentElement != null, "collectionInfo != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomTitleElementName, "Expected element named 'title'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Element 'title' should be in the atom namespace.");

            AtomTextConstruct titleTextConstruct = this.ReadTitleElement();

            if (odataServiceDocumentElement.Name == null)
            {
                odataServiceDocumentElement.Name = titleTextConstruct.Text;
            }

            if (this.ReadAtomMetadata)
            {
                collectionMetadata.Title = titleTextConstruct;
            }
        }