Ejemplo n.º 1
0
        internal static IEdmCollectionTypeReference AsCollectionOrNull(this IEdmTypeReference typeReference)
        {
            if (typeReference == null)
            {
                return(null);
            }
            if (typeReference.TypeKind() != EdmTypeKind.Collection)
            {
                return(null);
            }
            IEdmCollectionTypeReference reference = typeReference.AsCollection();

            if (!reference.IsNonEntityODataCollectionTypeKind())
            {
                return(null);
            }
            return(reference);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Read a collection from the reader.
        /// </summary>
        /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param>
        /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</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.Element    - the element was empty.
        ///                  XmlNodeType.EndElement - the element had some value.
        ///
        /// Note that this method will not read null values, those should be handled by the caller already.
        /// </remarks>
        private ODataCollectionValue ReadCollectionValue(
            IEdmCollectionTypeReference collectionTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);
            Debug.Assert(
                collectionTypeReference == null || collectionTypeReference.IsNonEntityODataCollectionTypeKind(),
                "If the metadata is specified it must denote a collection for this method to work.");

            this.IncreaseRecursionDepth();

            ODataCollectionValue collectionValue = new ODataCollectionValue();

            // If we have a metadata type for the collection, use that type name
            // otherwise use the type name from the payload (if there was any).
            collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName();
            if (serializationTypeNameAnnotation != null)
            {
                collectionValue.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();

            List <object> items = new List <object>();

            // Empty collections are valid - they have no items
            if (!this.XmlReader.IsEmptyElement)
            {
                // Read over the collection element to its first child node (or end-element)
                this.XmlReader.ReadStartElement();

                IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                DuplicatePropertyNamesChecker          duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                CollectionWithoutExpectedTypeValidator collectionValidator           = null;
                if (collectionTypeReference == null)
                {
                    // Parse the type name from the payload (if any), extract the item type name and construct a collection validator
                    string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName);
                    collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName);
                }

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                    case XmlNodeType.Element:
                        if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                        {
                            if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
                            {
                                throw new ODataException(o.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataNamespace));
                            }

                            // We don't support EPM for collections so it is fine to say that EPM is not present
                            object itemValue = this.ReadNonEntityValueImplementation(
                                itemTypeReference,
                                duplicatePropertyNamesChecker,
                                collectionValidator,
                                /*validateNullValue*/ true,
                                /*epmPresent*/ false);

                            // read over the end tag of the element or the start tag if the element was empty.
                            this.XmlReader.Read();

                            // Validate the item (for example that it's not null)
                            ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */);

                            // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                            // the expected type (the itemType).
                            items.Add(itemValue);
                        }
                        else
                        {
                            this.XmlReader.Skip();
                        }

                        break;

                    case XmlNodeType.EndElement:
                        // End of the collection.
                        break;

                    default:
                        // Non-element so for example a text node, just ignore
                        this.XmlReader.Skip();
                        break;
                    }
                }while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            }

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values.");

            this.DecreaseRecursionDepth();

            return(collectionValue);
        }
        /// <summary>
        /// Reads a collection value.
        /// </summary>
        /// <param name="collectionValueTypeReference">The collection type reference of the value.</param>
        /// <param name="payloadTypeName">The type name read from the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param>
        /// <returns>The value of the collection.</returns>
        /// <remarks>
        /// Pre-Condition:  Fails if the current node is not a JsonNodeType.StartObject
        /// Post-Condition: almost anything - the node after the collection value (after the EndObject)
        /// </remarks>
        private ODataCollectionValue ReadCollectionValueImplementation(
            IEdmCollectionTypeReference collectionValueTypeReference, 
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(
                collectionValueTypeReference == null || collectionValueTypeReference.IsNonEntityODataCollectionTypeKind(),
                "If the metadata is specified it must denote a Collection for this method to work.");
            this.JsonReader.AssertNotBuffering();

            ODataVersionChecker.CheckCollectionValue(this.Version);

            this.IncreaseRecursionDepth();

            // Read over the start object
            this.JsonReader.ReadStartObject();

            ODataCollectionValue collectionValue = new ODataCollectionValue();
            collectionValue.TypeName = collectionValueTypeReference != null ? collectionValueTypeReference.ODataFullName() : payloadTypeName;
            if (serializationTypeNameAnnotation != null)
            {
                collectionValue.SetAnnotation(serializationTypeNameAnnotation);
            }

            List<object> items = null;
            bool metadataPropertyFound = false;
            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                string propertyName = this.JsonReader.ReadPropertyName();

                if (string.CompareOrdinal(JsonConstants.ODataMetadataName, propertyName) == 0)
                {
                    // __metadata property
                    if (metadataPropertyFound)
                    {
                        throw new ODataException(o.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper(JsonConstants.ODataMetadataName));
                    }

                    metadataPropertyFound = true;

                    // Note that we don't need to read the type name again, as we've already read it above in FindTypeNameInPayload.
                    // There's nothing else of interest in the __metadata for collections.
                    this.JsonReader.SkipValue();
                }
                else if (string.CompareOrdinal(JsonConstants.ODataResultsName, propertyName) == 0)
                {
                    // results property
                    if (items != null)
                    {
                        throw new ODataException(o.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper(JsonConstants.ODataResultsName));
                    }

                    items = new List<object>();

                    this.JsonReader.ReadStartArray();

                    DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                    IEdmTypeReference itemType = null;
                    if (collectionValueTypeReference != null)
                    {
                        itemType = collectionValueTypeReference.CollectionDefinition().ElementType;
                    }

                    // NOTE: we do not support reading JSON without metadata right now so we always have an expected item type;
                    //       The collection validator is always null.
                    CollectionWithoutExpectedTypeValidator collectionValidator = null;

                    while (this.JsonReader.NodeType != JsonNodeType.EndArray)
                    {
                        object itemValue = this.ReadNonEntityValueImplementation(
                            itemType,
                            duplicatePropertyNamesChecker,
                            collectionValidator,
                            /*validateNullValue*/ true);

                        // Validate the item (for example that it's not null)
                        ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */);

                        // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                        // the expected type (the itemType).
                        items.Add(itemValue);
                    }

                    Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "The results value must end with an end array.");
                    this.JsonReader.ReadEndArray();
                }
                else
                {
                    // Skip over any other property in the collection object
                    this.JsonReader.SkipValue();
                }
            }

            Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndObject, "After all properties of Collection wrapper are read the EndObject node is expected.");
            this.JsonReader.ReadEndObject();

            if (items == null)
            {
                // We didn't find any results property. All collections must have the results property.
                throw new ODataException(o.Strings.ODataJsonPropertyAndValueDeserializer_CollectionWithoutResults);
            }

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.JsonReader.AssertNotBuffering();
            this.DecreaseRecursionDepth();

            return collectionValue;
        }
        /// <summary>
        /// Read a collection from the reader.
        /// </summary>
        /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param>
        /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param>
        /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</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.Element    - the element was empty.
        ///                  XmlNodeType.EndElement - the element had some value.
        ///                  
        /// Note that this method will not read null values, those should be handled by the caller already.
        /// </remarks>
        private ODataCollectionValue ReadCollectionValue(
            IEdmCollectionTypeReference collectionTypeReference, 
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute);
            Debug.Assert(
                collectionTypeReference == null || collectionTypeReference.IsNonEntityODataCollectionTypeKind(),
                "If the metadata is specified it must denote a collection for this method to work.");

            this.IncreaseRecursionDepth();

            ODataCollectionValue collectionValue = new ODataCollectionValue();

            // If we have a metadata type for the collection, use that type name
            // otherwise use the type name from the payload (if there was any).
            collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName();
            if (serializationTypeNameAnnotation != null)
            {
                collectionValue.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();

            List<object> items = new List<object>();

            // Empty collections are valid - they have no items
            if (!this.XmlReader.IsEmptyElement)
            {
                // Read over the collection element to its first child node (or end-element)
                this.XmlReader.ReadStartElement();

                IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                CollectionWithoutExpectedTypeValidator collectionValidator = null;
                if (collectionTypeReference == null)
                {
                    // Parse the type name from the payload (if any), extract the item type name and construct a collection validator
                    string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName);
                    collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName);
                }

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                        case XmlNodeType.Element:
                            if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                            {
                                if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
                                {
                                    throw new ODataException(o.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataNamespace));
                                }

                                // We don't support EPM for collections so it is fine to say that EPM is not present
                                object itemValue = this.ReadNonEntityValueImplementation(
                                    itemTypeReference, 
                                    duplicatePropertyNamesChecker, 
                                    collectionValidator,
                                    /*validateNullValue*/ true,
                                    /*epmPresent*/ false);

                                // read over the end tag of the element or the start tag if the element was empty.
                                this.XmlReader.Read();

                                // Validate the item (for example that it's not null)
                                ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */);

                                // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                                // the expected type (the itemType).
                                items.Add(itemValue);
                            }
                            else
                            {
                                this.XmlReader.Skip();
                            }

                            break;

                        case XmlNodeType.EndElement:
                            // End of the collection.
                            break;

                        default:
                            // Non-element so for example a text node, just ignore
                            this.XmlReader.Skip();
                            break;
                    }
                }
                while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            }

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.AssertXmlCondition(true, XmlNodeType.EndElement);
            Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values.");

            this.DecreaseRecursionDepth();

            return collectionValue;
        }