/// <summary>
        /// This method creates and reads the property from the input and
        /// returns an <see cref="ODataProperty"/> representing the read property.
        /// </summary>
        /// <param name="expectedProperty">The <see cref="IEdmProperty"/> producing the property to be read.</param>
        /// <param name="expectedPropertyTypeReference">The expected type of the property to read.</param>
        /// <returns>An <see cref="ODataProperty"/> representing the read property.</returns>
        internal ODataProperty ReadTopLevelProperty(IEdmStructuralProperty expectedProperty, IEdmTypeReference expectedPropertyTypeReference)
        {
            Debug.Assert(
                expectedPropertyTypeReference == null || !expectedPropertyTypeReference.IsODataEntityTypeKind(),
                "If the expected type is specified it must not be an entity type.");
            Debug.Assert(this.XmlReader != null, "this.xmlReader != null");

            this.ReadPayloadStart();
            Debug.Assert(this.XmlReader.NodeType == XmlNodeType.Element, "The XML reader must be positioned on an Element.");

            // For compatibility with WCF DS Server we need to be able to read the property element in any namespace, not just the OData namespace.
            if (!this.UseServerFormatBehavior && !this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace))
            {
                throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_TopLevelPropertyElementWrongNamespace(this.XmlReader.NamespaceURI, this.XmlReader.ODataMetadataNamespace));
            }

            // this is a top level property so EPM does not apply hence it is safe to say that EPM is not present
            this.AssertRecursionDepthIsZero();
            string        expectedPropertyName = ReaderUtils.GetExpectedPropertyName(expectedProperty);
            ODataProperty property             = this.ReadProperty(
                true,
                expectedPropertyName,
                expectedPropertyTypeReference,
                /*nullValueReadBehaviorKind*/ ODataNullValueBehaviorKind.Default);

            this.AssertRecursionDepthIsZero();

            Debug.Assert(property != null, "If we don't ignore null values the property must not be null.");

            this.ReadPayloadEnd();

            return(property);
        }
예제 #2
0
        /// <summary>
        /// This method creates an reads the property from the input and
        /// returns an <see cref="ODataProperty"/> representing the read property.
        /// </summary>
        /// <param name="expectedProperty">The <see cref="IEdmProperty"/> producing the property to be read.</param>
        /// <param name="expectedPropertyTypeReference">The expected type reference of the property to read.</param>
        /// <returns>An <see cref="ODataProperty"/> representing the read property.</returns>
        internal ODataProperty ReadTopLevelProperty(IEdmStructuralProperty expectedProperty, IEdmTypeReference expectedPropertyTypeReference)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None, the reader must not have been used yet.");
            Debug.Assert(
                expectedPropertyTypeReference == null || !expectedPropertyTypeReference.IsODataEntityTypeKind(),
                "If the expected type is specified it must not be an entity type.");
            this.JsonReader.AssertNotBuffering();

            if (!this.Model.IsUserModel())
            {
                throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_TopLevelPropertyWithoutMetadata);
            }

            // Read the response wrapper "d" if expected.
            this.ReadPayloadStart(false /*isReadingNestedPayload*/);

            string expectedPropertyName = ReaderUtils.GetExpectedPropertyName(expectedProperty);

            string propertyName  = null;
            object propertyValue = null;

            // We have to support reading top-level complex values without the { "property": ... } wrapper for open properties
            // in WCF DS Server backward compat mode. (Open property == property without expected type for us).
            if (this.ShouldReadTopLevelPropertyValueWithoutPropertyWrapper(expectedPropertyTypeReference))
            {
                // We will report the value without property wrapper as a property with empty name (this is technically invalid, but it will only happen in WCF DS Server mode).
                propertyName = expectedPropertyName ?? string.Empty;

                // Read the value directly
                propertyValue = this.ReadNonEntityValue(
                    expectedPropertyTypeReference,
                    /*duplicatePropertyNamesChecker*/ null,
                    /*collectionValidator*/ null,
                    /*validateNullValue*/ true,
                    propertyName);
            }
            else
            {
                // Read the start of the object container around the property { "property": ... }
                this.JsonReader.ReadStartObject();

                // Read through all top-level properties, ignore the ones with reserved names (i.e., reserved
                // characters in their name) and throw if we find none or more than one properties without reserved name.
                bool   foundProperty     = false;
                string foundPropertyName = null;
                while (this.JsonReader.NodeType == JsonNodeType.Property)
                {
                    // Read once - this should be the property
                    propertyName = this.JsonReader.ReadPropertyName();

                    if (!ValidationUtils.IsValidPropertyName(propertyName))
                    {
                        // We ignore properties with an invalid name since these are extension points for the future.
                        this.JsonReader.SkipValue();
                    }
                    else
                    {
                        if (foundProperty)
                        {
                            // There should be only one property in the top-level property wrapper that does not have a reserved name.
                            throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_InvalidTopLevelPropertyPayload);
                        }

                        foundProperty     = true;
                        foundPropertyName = propertyName;

                        // Now read the property value
                        propertyValue = this.ReadNonEntityValue(
                            expectedPropertyTypeReference,
                            /*duplicatePropertyNamesChecker*/ null,
                            /*collectionValidator*/ null,
                            /*validateNullValue*/ true,
                            propertyName);
                    }
                }

                if (!foundProperty)
                {
                    // No property found; there should be exactly one property in the top-level property wrapper that does not have a reserved name.
                    throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_InvalidTopLevelPropertyPayload);
                }

                Debug.Assert(foundPropertyName != null, "foundPropertyName != null");
                ReaderValidationUtils.ValidateExpectedPropertyName(expectedPropertyName, foundPropertyName);
                propertyName = foundPropertyName;

                // Read over the end object - note that this might be the last node in the input (in case there's no response wrapper)
                this.JsonReader.Read();
            }

            // Read the end of the response wrapper "d" if expected.
            this.ReadPayloadEnd(false /*isReadingNestedPayload*/);

            Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndOfInput, "Post-Condition: expected JsonNodeType.EndOfInput");
            this.JsonReader.AssertNotBuffering();

            Debug.Assert(propertyName != null, "propertyName != null");

            return(new ODataProperty
            {
                Name = propertyName,
                Value = propertyValue
            });
        }