/// <summary>
        /// Creates a new instance of an <see cref="ODataEdmCollectionValue"/>.
        /// </summary>
        /// <param name="collectionValue">The <see cref="ODataCollectionValue"/> to create the collection value for.</param>
        internal ODataEdmCollectionValue(ODataCollectionValue collectionValue)
            : base(collectionValue.GetEdmType())
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");

            this.collectionValue = collectionValue;
        }
        public void WritingCollectionValueShouldFailWhenNoTypeSpecified()
        {
            var serializer = CreateODataJsonLightValueSerializer(false);

            var collectionValue = new ODataCollectionValue() { Items = new List<string>() };

            Action test = () => serializer.WriteCollectionValue(collectionValue, null, false, false, false);

            test.ShouldThrow<ODataException>().WithMessage(Strings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForCollectionValueInRequest);
        }
        public void WritingCollectionValueDifferingFromExpectedTypeShouldFail_WithoutEdmPrefix()
        {
            var serializer = CreateODataJsonLightValueSerializer(true);

            var collectionValue = new ODataCollectionValue() { Items = new List<string>(), TypeName = "Collection(Int32)" };

            var stringCollectionTypeRef = EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(true));
            Action test = () => serializer.WriteCollectionValue(collectionValue, stringCollectionTypeRef, false, false, false);

            test.ShouldThrow<ODataException>().WithMessage(Strings.ValidationUtils_IncompatibleType("Collection(Edm.Int32)", "Collection(Edm.String)"));
        }
        public void PrimitiveTypeCollectionRoundtripAtomTest()
        {
            ODataCollectionValue primitiveCollectionValue = new ODataCollectionValue {TypeName = "Collection(Edm.String)", Items = new[]{"Basketball", "Swimming"}};
            ODataFeedAndEntrySerializationInfo info = new ODataFeedAndEntrySerializationInfo()
            {
                NavigationSourceEntityTypeName = "Edm.String",
                NavigationSourceName = "Hobbies",
                ExpectedTypeName = "Edm.String"
            };

            this.VerifyTypeCollectionRoundtrip(primitiveCollectionValue, "Hobbies", info);
        }
        public void WritingCollectionValueElementWithNullValue()
        {
            var serializer = CreateODataJsonLightValueSerializer(true);

            var collectionValue = new ODataCollectionValue()
                {
                    Items = new int?[] { null },
                    TypeName = "Collection(Edm.Int32)"
                };

            var stringCollectionTypeRef = EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetInt32(true));
            serializer.WriteCollectionValue(collectionValue, stringCollectionTypeRef, false, false, false);
        }
        public void ComplexTypeCollectionRoundtripAtomTest()
        {
            ODataComplexValue subject0 = new ODataComplexValue() { TypeName = "NS.Subject", Properties = new[] { new ODataProperty() { Name = "Name", Value = "English" }, new ODataProperty() { Name = "Score", Value = (Int16)98 } } };
            ODataComplexValue subject1 = new ODataComplexValue() { TypeName = "NS.Subject", Properties = new[] { new ODataProperty() { Name = "Name", Value = "Math" }, new ODataProperty() { Name = "Score", Value = (Int16)90 } } };
            ODataCollectionValue complexCollectionValue = new ODataCollectionValue { TypeName = "Collection(NS.Subject)", Items = new[] { subject0, subject1 } };
            ODataFeedAndEntrySerializationInfo info = new ODataFeedAndEntrySerializationInfo() {
                NavigationSourceEntityTypeName = subject0.TypeName,
                NavigationSourceName = "Subjects",
                ExpectedTypeName = subject0.TypeName
            };

            this.VerifyTypeCollectionRoundtrip(complexCollectionValue, "Subjects", info);
        }
 public void WritingAComplexCollectionValuedAnnotationWithMetadataShouldWork()
 {
     ODataComplexValue complex = new ODataComplexValue { Properties = new[] { new ODataProperty { Name = "p1", Value = 123 } } };
     ODataCollectionValue collection = new ODataCollectionValue { Items = new[] { complex }, TypeName = "Collection(ns.complex2)" };
     var annotation = AtomInstanceAnnotation.CreateFrom(new ODataInstanceAnnotation("custom.complexCollection", collection), /*target*/ null);
     this.serializer.WriteInstanceAnnotation(annotation);
     this.ValidatePayload("<annotation term=\"custom.complexCollection\" m:type=\"#Collection(ns.complex2)\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\"><m:element><d:p1 m:type=\"Int32\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\">123</d:p1></m:element></annotation>");
 }
        public void WritingNonNullableCollectionValueWithNullElementShouldThrow()
        {
            EdmModel model = new EdmModel();
            EdmEntityType entityType = new EdmEntityType("NS", "MyTestEntity");
            EdmStructuralProperty key =
                entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32, false);
            entityType.AddKeys(key);
            entityType.AddStructuralProperty(
                "NonNullableIntNumbers",
                new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetInt32(false))));
            model.AddElement(entityType);

            var collectionValue = new ODataCollectionValue()
            {
                Items = new int?[] { null },
                TypeName = "Collection(Edm.Int32)"
            };

            ODataEntry entry = new ODataEntry()
            {
                Id = new Uri("http://test/Id"),
                TypeName = "NS.MyTestEntity",
                Properties = new[]
                {
                    new ODataProperty { Name = "NonNullableIntNumbers", Value = collectionValue },
                },
                SerializationInfo = PropertyAndValueAtomWriterIntegrationTests.MySerializationInfo
            };

            Action test = () => this.WriterEntry(model, entry, entityType);

            test.ShouldThrow<ODataException>().WithMessage(
                Strings.ValidationUtils_NonNullableCollectionElementsMustNotBeNull);
        }
        /// <summary>
        /// Writes a collection property.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="collectionValue">The collection value to be written</param>
        /// <param name="propertyTypeReference">The metadata type reference of the property.</param>
        /// <param name="isTopLevel">true when writing a top-level property; false for nested properties.</param>
        /// <param name="isOpenPropertyType">If the property is open.</param>
        private void WriteCollectionProperty(
            ODataProperty property,
            ODataCollectionValue collectionValue,
            IEdmTypeReference propertyTypeReference,
            bool isTopLevel,
            bool isOpenPropertyType)
        {
            string wirePropertyName = GetWirePropertyName(isTopLevel, property.Name);

            IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator);
            string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, propertyTypeReference, typeFromValue, isOpenPropertyType);

            this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
            this.JsonWriter.WriteName(wirePropertyName);

            // passing false for 'isTopLevel' because the outer wrapping object has already been written.
            this.JsonLightValueSerializer.WriteCollectionValue(collectionValue, propertyTypeReference, typeFromValue, isTopLevel, false /*isInUri*/, isOpenPropertyType);
            return;
        }
示例#10
0
 /// <summary>
 /// Visits a collection item.
 /// </summary>
 /// <param name="collection">The collection to visit.</param>
 protected override void VisitCollectionValue(ODataCollectionValue collection)
 {
     this.ValidateEnumerable(collection.Items, "ODataCollectionValue.Items");
     base.VisitCollectionValue(collection);
 }
        /// <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.StartArray
        /// Post-Condition: almost anything - the node after the collection value (after the EndArray)
        /// </remarks>
        private ODataCollectionValue ReadCollectionValue(
            IEdmCollectionTypeReference collectionValueTypeReference,
            string payloadTypeName,
            SerializationTypeNameAnnotation serializationTypeNameAnnotation)
        {
            Debug.Assert(
                collectionValueTypeReference == null || collectionValueTypeReference.IsNonEntityCollectionType(),
                "If the metadata is specified it must denote a Collection for this method to work.");

            this.IncreaseRecursionDepth();

            // Read over the start array
            this.JsonReader.ReadStartArray();

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

            if (collectionValueTypeReference != null)
            {
                collectionValue.SetAnnotation(new ODataTypeAnnotation(collectionValueTypeReference));
            }

            List<object> items = new List<object>();
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
            IEdmTypeReference itemType = null;
            if (collectionValueTypeReference != null)
            {
                itemType = collectionValueTypeReference.CollectionDefinition().ElementType;
            }

            // NOTE: we do not support reading JSON Light 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(
                    /*payloadTypeName*/ null,
                    itemType,
                    duplicatePropertyNamesChecker,
                    collectionValidator,
                    /*validateNullValue*/ true,
                    /*isTopLevelPropertyValue*/ false,
                    /*insideComplexValue*/ false,
                    /*propertyName*/ null);

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

                // 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();

            collectionValue.Items = new ReadOnlyEnumerable(items);

            this.DecreaseRecursionDepth();

            return collectionValue;
        }
 public void BuildPropertyContextUriForCollectionPropertyValueWithNonNullAnnotation()
 {
     ODataCollectionValue value = new ODataCollectionValue { TypeName = "FQNS.FromObject" };
     value.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = "FQNS.FromAnnotation" });
     var contextUri = this.CreatePropertyContextUri(value);
     contextUri.OriginalString.Should().Be(BuildExpectedContextUri("#FQNS.FromAnnotation"));
 }
        /// <summary>
        /// Writes a name/value pair for a property.
        /// </summary>
        /// <param name="property">The property to write out.</param>
        /// <param name="owningType">The <see cref="IEdmStructuredType"/> of the entry or complex type containing the property (or null if not metadata is available).</param>
        /// <param name="allowStreamProperty">Should pass in true if we are writing a property of an ODataEntry instance, false otherwise.
        /// Named stream properties should only be defined on ODataEntry instances.</param>
        /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param>
        /// <param name="projectedProperties">Set of projected properties, or null if all properties should be written.</param>
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            DebugUtils.CheckNoExternalCallers();

            WriterValidationUtils.ValidatePropertyNotNull(property);

            string propertyName = property.Name;
            object value        = property.Value;

            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            WriterValidationUtils.ValidatePropertyName(propertyName);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);
            IEdmProperty      edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningType, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            // If the property is of Geography or Geometry type or the value is of Geography or Geometry type
            // make sure to check that the version is 3.0 or above.
            if ((propertyTypeReference != null && propertyTypeReference.IsSpatial()) ||
                (propertyTypeReference == null && value is System.Spatial.ISpatial))
            {
                ODataVersionChecker.CheckSpatialValue(this.Version);
            }

            this.JsonWriter.WriteName(propertyName);
            if (value == null)
            {
                WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.MessageWriterSettings.WriterBehavior, this.Model);
                this.JsonWriter.WriteValue(null);
            }
            else
            {
                bool isOpenPropertyType = owningType != null && owningType.IsOpen && propertyTypeReference == null;
                if (isOpenPropertyType)
                {
                    ValidationUtils.ValidateOpenPropertyValue(propertyName, value, this.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
                }

                ODataComplexValue complexValue = value as ODataComplexValue;
                if (complexValue != null)
                {
                    this.WriteComplexValue(
                        complexValue,
                        propertyTypeReference,
                        isOpenPropertyType,
                        this.CreateDuplicatePropertyNamesChecker(),
                        /*collectionValidator*/ null);
                }
                else
                {
                    ODataCollectionValue collectionValue = value as ODataCollectionValue;
                    if (collectionValue != null)
                    {
                        ODataVersionChecker.CheckCollectionValueProperties(this.Version, propertyName);
                        this.WriteCollectionValue(
                            collectionValue,
                            propertyTypeReference,
                            isOpenPropertyType);
                    }
                    else
                    {
                        ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;
                        if (streamReferenceValue != null)
                        {
                            if (!allowStreamProperty)
                            {
                                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                            }

                            Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                            WriterValidationUtils.ValidateStreamReferenceProperty(property, edmProperty, this.Version, this.WritingResponse);
                            WriterValidationUtils.ValidateStreamReferenceValue(streamReferenceValue, /*isDefaultStream*/ false);
                            this.WriteStreamReferenceValue(streamReferenceValue);
                        }
                        else
                        {
                            this.WritePrimitiveValue(value, /*collectionValidator*/ null, propertyTypeReference);
                        }
                    }
                }
            }
        }
示例#14
0
        public override object Read(ODataMessageReader messageReader, Type type, ODataDeserializerContext readContext)
        {
            if (messageReader == null)
            {
                throw Error.ArgumentNull("messageReader");
            }

            IEdmAction action = GetAction(readContext);

            Contract.Assert(action != null);

            // Create the correct resource type;
            Dictionary <string, object> payload;

            if (type == typeof(ODataActionParameters))
            {
                payload = new ODataActionParameters();
            }
            else
            {
                payload = new ODataUntypedActionParameters(action);
            }

            ODataParameterReader reader = messageReader.CreateODataParameterReader(action);

            while (reader.Read())
            {
                string parameterName             = null;
                IEdmOperationParameter parameter = null;

                switch (reader.State)
                {
                case ODataParameterReaderState.Value:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    // ODataLib protects against this but asserting just in case.
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    if (parameter.Type.IsPrimitive())
                    {
                        payload[parameterName] = reader.Value;
                    }
                    else
                    {
                        ODataEdmTypeDeserializer deserializer = DeserializerProvider.GetEdmTypeDeserializer(parameter.Type);
                        payload[parameterName] = deserializer.ReadInline(reader.Value, parameter.Type, readContext);
                    }
                    break;

                case ODataParameterReaderState.Collection:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    // ODataLib protects against this but asserting just in case.
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    IEdmCollectionTypeReference collectionType = parameter.Type as IEdmCollectionTypeReference;
                    Contract.Assert(collectionType != null);
                    ODataCollectionValue        value = ODataCollectionDeserializer.ReadCollection(reader.CreateCollectionReader());
                    ODataCollectionDeserializer collectionDeserializer = (ODataCollectionDeserializer)DeserializerProvider.GetEdmTypeDeserializer(collectionType);
                    payload[parameterName] = collectionDeserializer.ReadInline(value, collectionType, readContext);
                    break;

                case ODataParameterReaderState.Entry:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));

                    IEdmEntityTypeReference entityTypeReference = parameter.Type as IEdmEntityTypeReference;
                    Contract.Assert(entityTypeReference != null);

                    ODataReader             entryReader        = reader.CreateEntryReader();
                    object                  item               = ODataEntityDeserializer.ReadEntryOrFeed(entryReader);
                    ODataEntityDeserializer entityDeserializer = (ODataEntityDeserializer)DeserializerProvider.GetEdmTypeDeserializer(entityTypeReference);
                    payload[parameterName] = entityDeserializer.ReadInline(item, entityTypeReference, readContext);
                    break;

                case ODataParameterReaderState.Feed:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));

                    IEdmCollectionTypeReference feedType = parameter.Type as IEdmCollectionTypeReference;
                    Contract.Assert(feedType != null);

                    ODataReader           feedReader       = reader.CreateFeedReader();
                    object                feed             = ODataEntityDeserializer.ReadEntryOrFeed(feedReader);
                    ODataFeedDeserializer feedDeserializer = (ODataFeedDeserializer)DeserializerProvider.GetEdmTypeDeserializer(feedType);

                    object result = feedDeserializer.ReadInline(feed, feedType, readContext);

                    IEdmTypeReference elementTypeReference = feedType.ElementType();
                    Contract.Assert(elementTypeReference.IsEntity());

                    IEnumerable enumerable = result as IEnumerable;
                    if (enumerable != null)
                    {
                        if (readContext.IsUntyped)
                        {
                            EdmEntityObjectCollection entityCollection = new EdmEntityObjectCollection(feedType);
                            foreach (EdmEntityObject entityObject in enumerable)
                            {
                                entityCollection.Add(entityObject);
                            }

                            payload[parameterName] = entityCollection;
                        }
                        else
                        {
                            Type        elementClrType = EdmLibHelpers.GetClrType(elementTypeReference, readContext.Model);
                            IEnumerable castedResult   =
                                _castMethodInfo.MakeGenericMethod(elementClrType)
                                .Invoke(null, new[] { result }) as IEnumerable;
                            payload[parameterName] = castedResult;
                        }
                    }
                    break;
                }
            }

            return(payload);
        }
示例#15
0
        /// <summary>
        /// Converts a <see cref="ODataCollectionValue"/> to a string for use in a Url.
        /// </summary>
        /// <param name="collectionValue">Instance to convert.</param>
        /// <param name="model">Model to be used for validation. User model is optional. The EdmLib core model is expected as a minimum.</param>
        /// <param name="version">Version to be compliant with. Collection requires >= V3.</param>
        /// <returns>A string representation of <paramref name="collectionValue"/> to be added to a Url.</returns>
        internal static string ConvertToUriCollectionLiteral(ODataCollectionValue collectionValue, IEdmModel model, ODataVersion version)
        {
            ExceptionUtils.CheckArgumentNotNull(collectionValue, "collectionValue");
            ExceptionUtils.CheckArgumentNotNull(model, "model");

            StringBuilder builder = new StringBuilder();
            using (TextWriter textWriter = new StringWriter(builder, CultureInfo.InvariantCulture))
            {
                ODataMessageWriterSettings messageWriterSettings = new ODataMessageWriterSettings()
                {
                    Version = version,
                    Indent = false
                };

                WriteJsonLightLiteral(
                    model,
                    messageWriterSettings,
                    textWriter,
                    (serializer) => serializer.WriteCollectionValue(
                        collectionValue,
                        null /*metadataTypeReference*/,
                        null /*valueTypeReference*/,
                        false /*isTopLevelProperty*/,
                        true /*isInUri*/,
                        false /*isOpenPropertyType*/));
            }

            return builder.ToString();
        }
        /// <summary>
        /// Writes out the value of a collection property.
        /// </summary>
        /// <param name="collectionValue">The collection value to write.</param>
        /// <param name="metadataTypeReference">The metadata type reference for the collection.</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <remarks>The current recursion depth is measured by the number of complex and collection values between
        /// this one and the top-level payload, not including this one.</remarks>
        internal void WriteCollectionValue(
            ODataCollectionValue collectionValue,
            IEdmTypeReference metadataTypeReference,
            bool isOpenPropertyType)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(collectionValue != null, "collectionValue != null");

            this.IncreaseRecursionDepth();

            // Start the object scope which will represent the entire CollectionValue instance
            this.JsonWriter.StartObjectScope();

            // resolve the type name to the type; if no type name is specified we will use the
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, collectionValue, isOpenPropertyType);

            // "__metadata": { "type": "typename" }
            // If the CollectionValue has type information write out the metadata and the type in it.
            string collectionItemTypeName;
            string typeName = this.VerboseJsonOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName);

            if (typeName != null)
            {
                ODataJsonWriterUtils.WriteMetadataWithTypeName(this.JsonWriter, typeName);
            }

            // "results": [
            // This represents the array of items in the CollectionValue
            this.JsonWriter.WriteDataArrayName();
            this.JsonWriter.StartArrayScope();

            // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration)
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName);

                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, false /* isStreamable */);

                    ODataComplexValue itemAsComplexValue = item as ODataComplexValue;
                    if (itemAsComplexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            itemAsComplexValue,
                            expectedItemTypeReference,
                            false,
                            duplicatePropertyNamesChecker,
                            collectionValidator);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference);
                    }
                }
            }

            // End the array scope which holds the items
            this.JsonWriter.EndArrayScope();

            // End the object scope which holds the entire collection
            this.JsonWriter.EndObjectScope();

            this.DecreaseRecursionDepth();
        }
        /// <summary>
        /// Asynchronously writes the ODataValue (primitive, collection or resource value) to the underlying json writer.
        /// </summary>
        /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param>
        /// <param name="odataValue">value to write.</param>
        /// <returns>A task that represents the asynchronous write operation.</returns>
        internal static async Task WriteODataValueAsync(this IJsonWriterAsync jsonWriter, ODataValue odataValue)
        {
            if (odataValue == null || odataValue is ODataNullValue)
            {
                await jsonWriter.WriteValueAsync((string)null).ConfigureAwait(false);

                return;
            }

            object objectValue = odataValue.FromODataValue();

            if (EdmLibraryExtensions.IsPrimitiveType(objectValue.GetType()))
            {
                await jsonWriter.WritePrimitiveValueAsync(objectValue).ConfigureAwait(false);

                return;
            }

            ODataResourceValue resourceValue = odataValue as ODataResourceValue;

            if (resourceValue != null)
            {
                await jsonWriter.StartObjectScopeAsync().ConfigureAwait(false);

                foreach (ODataProperty property in resourceValue.Properties)
                {
                    await jsonWriter.WriteNameAsync(property.Name).ConfigureAwait(false);

                    await jsonWriter.WriteODataValueAsync(property.ODataValue).ConfigureAwait(false);
                }

                await jsonWriter.EndObjectScopeAsync().ConfigureAwait(false);

                return;
            }

            ODataCollectionValue collectionValue = odataValue as ODataCollectionValue;

            if (collectionValue != null)
            {
                await jsonWriter.StartArrayScopeAsync().ConfigureAwait(false);

                foreach (object item in collectionValue.Items)
                {
                    // Will not be able to accurately serialize complex objects unless they are ODataValues.
                    ODataValue collectionItem = item as ODataValue;
                    if (item != null)
                    {
                        await jsonWriter.WriteODataValueAsync(collectionItem).ConfigureAwait(false);
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.ODataJsonWriter_UnsupportedValueInCollection);
                    }
                }

                await jsonWriter.EndArrayScopeAsync().ConfigureAwait(false);

                return;
            }

            throw new ODataException(
                      ODataErrorStrings.ODataJsonWriter_UnsupportedValueType(odataValue.GetType().FullName));
        }
示例#18
0
        /// <summary>
        /// Create new ODataComplexValue from the old, to discard all null/zero values
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private ODataComplexValue RebuildComplexValue(ODataComplexValue source)
        {
            ODataComplexValue newVal = new ODataComplexValue();

            newVal.TypeName = source.TypeName;

            List <ODataProperty> complexSons = source.Properties.ToList();

            //Filter to get new list
            List <ODataProperty> filteredSons = new List <ODataProperty>();

            foreach (ODataProperty prop in complexSons)
            {
                PropertyType retType = GetPropertyType(prop);
                switch (retType)
                {
                case PropertyType.SimpleEdmx:
                {
                    if (null != prop.Value)
                    {
                        if (prop.Value.GetType().Name == "Int32")
                        {
                            //Check the value now.
                            bool bInclude = false;
                            try
                            {
                                //TODO: You cannot simply do this, potential bugs there maybe.
                                //Use your own logics the determine if need to ignore ZEORs or not.
                                int val = Convert.ToInt32(prop.Value);
                                bInclude = (0 != val);
                            }
                            catch (Exception)
                            {
                            }

                            if (bInclude)
                            {
                                filteredSons.Add(prop);
                            }
                        }
                        else
                        {
                            filteredSons.Add(prop);
                        }
                    }
                }
                break;


                case PropertyType.ComplexType:
                {
                    //Recursively
                    ODataComplexValue comx = RebuildComplexValue((ODataComplexValue)prop.Value);
                    if (comx.Properties.Count() > 0)
                    {
                        prop.Value = comx;
                        filteredSons.Add(prop);
                    }
                }
                break;


                case PropertyType.Collection:
                {
                    ODataCollectionValue     coll     = RebuildCollectionValue((ODataCollectionValue)prop.Value);
                    List <ODataComplexValue> listSubs = (List <ODataComplexValue>)coll.Items;
                    if (listSubs.Count > 0)
                    {
                        prop.Value = coll;
                        filteredSons.Add(prop);
                    }
                }
                break;


                default:
                    break;
                }
            }

            //Re-Assign sons
            newVal.Properties = filteredSons;

            return(newVal);
        }
        public void CreateODataComplexValue_WritesDynamicCollectionProperty_ForOpenComplexType()
        {
            // Arrange
            IEdmModel model = SerializationTestsHelpers.SimpleOpenTypeModel();

            IEdmComplexType addressType       = model.FindDeclaredType("Default.Address") as IEdmComplexType;
            Type            simpleOpenAddress = typeof(SimpleOpenAddress);

            model.SetAnnotationValue <ClrTypeAnnotation>(addressType, new ClrTypeAnnotation(simpleOpenAddress));

            IEdmEnumType enumType       = model.FindDeclaredType("Default.SimpleEnum") as IEdmEnumType;
            Type         simpleEnumType = typeof(SimpleEnum);

            model.SetAnnotationValue <ClrTypeAnnotation>(enumType, new ClrTypeAnnotation(simpleEnumType));

            model.SetAnnotationValue(addressType, new DynamicPropertyDictionaryAnnotation(
                                         simpleOpenAddress.GetProperty("Properties")));

            IEdmComplexTypeReference addressTypeRef = addressType.ToEdmTypeReference(isNullable: false).AsComplex();

            ODataSerializerProvider    serializerProvider = new DefaultODataSerializerProvider();
            ODataComplexTypeSerializer serializer         = new ODataComplexTypeSerializer(serializerProvider);
            ODataSerializerContext     context            = new ODataSerializerContext
            {
                Model = model
            };

            SimpleOpenAddress address = new SimpleOpenAddress()
            {
                Street     = "One Way",
                City       = "One City",
                Properties = new Dictionary <string, object>()
            };

            address.Properties.Add("CollectionProperty", new[] { 1.1, 2.2 });

            // Act
            var odataValue = serializer.CreateODataComplexValue(address, addressTypeRef, context);

            // Assert
            ODataComplexValue complexValue = Assert.IsType <ODataComplexValue>(odataValue);

            Assert.Equal(complexValue.TypeName, "Default.Address");
            Assert.Equal(3, complexValue.Properties.Count());

            // Verify the declared properties
            ODataProperty street = Assert.Single(complexValue.Properties.Where(p => p.Name == "Street"));

            Assert.Equal("One Way", street.Value);

            ODataProperty city = Assert.Single(complexValue.Properties.Where(p => p.Name == "City"));

            Assert.Equal("One City", city.Value);

            // Verify the dynamic properties
            ODataProperty        enumProperty    = Assert.Single(complexValue.Properties.Where(p => p.Name == "CollectionProperty"));
            ODataCollectionValue collectionValue = Assert.IsType <ODataCollectionValue>(enumProperty.Value);

            Assert.Equal("Collection(Edm.Double)", collectionValue.TypeName);
            Assert.Equal(new List <double> {
                1.1, 2.2
            }, collectionValue.Items.OfType <double>().ToList());
        }
 public void WritingAComplexCollectionValuedAnnotationWithMismatchedMetadataShouldThrow()
 {
     ODataComplexValue complex = new ODataComplexValue { Properties = new[] { new ODataProperty { Name = "p1", Value = 123 } } };
     ODataCollectionValue collection = new ODataCollectionValue { Items = new[] { complex }, TypeName = "Collection(ns.complex1)" };
     Action test = () => this.serializer.WriteInstanceAnnotation(AtomInstanceAnnotation.CreateFrom(new ODataInstanceAnnotation("custom.complexCollection", collection), /*target*/ null));
     test.ShouldThrow<ODataException>().WithMessage(Strings.ValidationUtils_IncompatibleType("Collection(ns.complex1)", "Collection(ns.complex2)"));
 }
 /// <summary>
 /// Visits a collection item.
 /// </summary>
 /// <param name="collectionValue">The collection to visit.</param>
 protected abstract T VisitCollectionValue(ODataCollectionValue collectionValue);
        public void SerializeTopLevelPropertyOfCollectionTypeShouldWork()
        {
            EdmModel model = new EdmModel();

            EdmComplexType complexType = new EdmComplexType("ns", "complex");
            complexType.AddProperty(new EdmStructuralProperty(complexType, "propertyName1", EdmCoreModel.Instance.GetInt32(isNullable: false)));
            complexType.AddProperty(new EdmStructuralProperty(complexType, "propertyName2", EdmCoreModel.Instance.GetString(isNullable: false)));
            model.AddElement(complexType);

            ODataCollectionValue primitiveCollectionValue = new ODataCollectionValue { Items = new[] { "value1", "value2" }};
            primitiveCollectionValue.TypeName = "Collection(Edm.String)";
            ODataProperty primitiveCollectionProperty = new ODataProperty { Name = "PrimitiveCollectionProperty", Value = primitiveCollectionValue };

            string pval = SerializeProperty(model, primitiveCollectionProperty);
            pval.Should().Be("<?xml version=\"1.0\" encoding=\"utf-8\"?><m:value xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/$metadata#Collection(Edm.String)\" m:type=\"#Collection(String)\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\"><m:element>value1</m:element><m:element>value2</m:element></m:value>");
            
            ODataComplexValue complexValue1 = new ODataComplexValue { Properties = new[] { new ODataProperty { Name = "propertyName1", Value = 1 }, new ODataProperty { Name = "propertyName2", Value = "stringValue" } }, TypeName = "ns.complex" };
            ODataComplexValue complexValue2 = new ODataComplexValue { Properties = new[] { new ODataProperty { Name = "propertyName1", Value = 1 }, new ODataProperty { Name = "propertyName2", Value = "stringValue" } }, TypeName = "ns.complex" };
            ODataCollectionValue complexCollectionValue = new ODataCollectionValue { Items = new[] { complexValue1, complexValue2 }, TypeName = "Collection(ns.complex)" };
            ODataProperty complexCollectionProperty = new ODataProperty { Name = "ComplexCollectionProperty", Value = complexCollectionValue };

            string cval = SerializeProperty(model, complexCollectionProperty);
            cval.Should().Be("<?xml version=\"1.0\" encoding=\"utf-8\"?><m:value xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\" m:context=\"http://odata.org/$metadata#Collection(ns.complex)\" m:type=\"#Collection(ns.complex)\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\"><m:element><d:propertyName1 m:type=\"Int32\">1</d:propertyName1><d:propertyName2>stringValue</d:propertyName2></m:element><m:element><d:propertyName1 m:type=\"Int32\">1</d:propertyName1><d:propertyName2>stringValue</d:propertyName2></m:element></m:value>");
        }
示例#23
0
        /// <summary>
        /// Creates an <see cref="ODataCollectionValue"/> for the enumerable represented by <paramref name="enumerable"/>.
        /// </summary>
        /// <param name="enumerable">The value of the collection to be created.</param>
        /// <param name="elementType">The element EDM type of the collection.</param>
        /// <param name="writeContext">The serializer context to be used while creating the collection.</param>
        /// <returns>The created <see cref="ODataCollectionValue"/>.</returns>
        public virtual async Task <ODataCollectionValue> CreateODataCollectionValueAsync(IEnumerable enumerable, IEdmTypeReference elementType,
                                                                                         ODataSerializerContext writeContext)
        {
            if (writeContext == null)
            {
                throw Error.ArgumentNull("writeContext");
            }
            if (elementType == null)
            {
                throw Error.ArgumentNull("elementType");
            }

            var valueCollection = new List <object>();

            if (enumerable != null)
            {
                ODataEdmTypeSerializer itemSerializer = null;
                foreach (object item in enumerable)
                {
                    if (item == null)
                    {
                        if (elementType.IsNullable)
                        {
                            valueCollection.Add(null);
                            continue;
                        }

                        throw new SerializationException(SRResources.NullElementInCollection);
                    }

                    IEdmTypeReference actualType = writeContext.GetEdmType(item, item.GetType());
                    Contract.Assert(actualType != null);

                    itemSerializer = itemSerializer ?? SerializerProvider.GetEdmTypeSerializer(actualType);
                    if (itemSerializer == null)
                    {
                        throw new SerializationException(
                                  Error.Format(SRResources.TypeCannotBeSerialized, actualType.FullName(),
                                               typeof(ODataOutputFormatter).Name));
                    }

                    // ODataCollectionWriter expects the individual elements in the collection to be the underlying
                    // values and not ODataValues.
                    valueCollection.Add(
                        (await itemSerializer.CreateODataValueAsync(item, actualType, writeContext)).GetInnerValue());
                }
            }

            // Ideally, we'd like to do this:
            // string typeName = _edmCollectionType.FullName();
            // But ODataLib currently doesn't support .FullName() for collections. As a workaround, we construct the
            // collection type name the hard way.
            string typeName = "Collection(" + elementType.FullName() + ")";

            // ODataCollectionValue is only a V3 property, arrays inside Complex Types or Entity types are only supported in V3
            // if a V1 or V2 Client requests a type that has a collection within it ODataLib will throw.
            ODataCollectionValue value = new ODataCollectionValue
            {
                Items    = valueCollection,
                TypeName = typeName
            };

            AddTypeNameAnnotationAsNeeded(value, writeContext.MetadataLevel);
            return(value);
        }
        public void WriteTopLevelErrorWithCollectionOfComplexInstanceAnnotation()
        {
            var result = SetupSerializerAndRunTest(null, serializer =>
            {
                ODataError error = new ODataError();
                var instanceAnnotations = new Collection<ODataInstanceAnnotation>();
                var collection = new ODataCollectionValue
                {
                    TypeName = "Collection(ns.ErrorDetails)",
                    Items = new[] { new ODataComplexValue(), new ODataComplexValue { TypeName = "ns.ErrorDetails" } }
                };
                ODataInstanceAnnotation annotation = new ODataInstanceAnnotation("sample.collection", collection);
                instanceAnnotations.Add(annotation);
                error.InstanceAnnotations = instanceAnnotations;

                serializer.WriteTopLevelError(error, false);
            });

            result.Should().Contain("\"[email protected]\":\"#Collection(ns.ErrorDetails)\",\"@sample.collection\":[{},{}]");
        }
示例#25
0
        /// <summary>
        /// Visits an item in the object model.
        /// </summary>
        /// <param name="objectModelItem">The item to visit.</param>
        public virtual void Visit(object objectModelItem)
        {
            ODataResourceSet resourceCollection = objectModelItem as ODataResourceSet;

            if (resourceCollection != null)
            {
                this.VisitFeed(resourceCollection);
                return;
            }

            ODataResource entry = objectModelItem as ODataResource;

            if (entry != null)
            {
                this.VisitEntry(entry);
                return;
            }

            ODataProperty property = objectModelItem as ODataProperty;

            if (property != null)
            {
                this.VisitProperty(property);
                return;
            }

            ODataNestedResourceInfo navigationLink = objectModelItem as ODataNestedResourceInfo;

            if (navigationLink != null)
            {
                this.VisitNavigationLink(navigationLink);
                return;
            }

            ODataResourceValue resourceValue = objectModelItem as ODataResourceValue;

            if (resourceValue != null)
            {
                this.VisitResourceValue(resourceValue);
                return;
            }

            ODataCollectionValue collectionValue = objectModelItem as ODataCollectionValue;

            if (collectionValue != null)
            {
                this.VisitCollectionValue(collectionValue);
                return;
            }

            ODataStreamReferenceValue streamReferenceValue = objectModelItem as ODataStreamReferenceValue;

            if (streamReferenceValue != null)
            {
                this.VisitStreamReferenceValue(streamReferenceValue);
                return;
            }

            ODataCollectionStart collectionStart = objectModelItem as ODataCollectionStart;

            if (collectionStart != null)
            {
                this.VisitCollectionStart(collectionStart);
                return;
            }

            ODataServiceDocument serviceDocument = objectModelItem as ODataServiceDocument;

            if (serviceDocument != null)
            {
                this.VisitServiceDocument(serviceDocument);
                return;
            }

            ODataEntitySetInfo entitySetInfo = objectModelItem as ODataEntitySetInfo;

            if (entitySetInfo != null)
            {
                this.VisitEntitySet(entitySetInfo);
                return;
            }

            ODataError error = objectModelItem as ODataError;

            if (error != null)
            {
                this.VisitError(error);
                return;
            }

            ODataInnerError innerError = objectModelItem as ODataInnerError;

            if (innerError != null)
            {
                this.VisitInnerError(innerError);
                return;
            }

            ODataEntityReferenceLinks entityReferenceLinks = objectModelItem as ODataEntityReferenceLinks;

            if (entityReferenceLinks != null)
            {
                this.VisitEntityReferenceLinks(entityReferenceLinks);
                return;
            }

            ODataEntityReferenceLink entityReferenceLink = objectModelItem as ODataEntityReferenceLink;

            if (entityReferenceLink != null)
            {
                this.VisitEntityReferenceLink(entityReferenceLink);
                return;
            }

            ODataAction action = objectModelItem as ODataAction;

            if (action != null)
            {
                this.VisitODataOperation(action);
                return;
            }

            ODataFunction function = objectModelItem as ODataFunction;

            if (function != null)
            {
                this.VisitODataOperation(function);
                return;
            }

            ODataParameters parameters = objectModelItem as ODataParameters;

            if (parameters != null)
            {
                this.VisitParameters(parameters);
                return;
            }

            ODataBatch batch = objectModelItem as ODataBatch;

            if (batch != null)
            {
                this.VisitBatch(batch);
                return;
            }

            ODataBatchChangeset batchChangeset = objectModelItem as ODataBatchChangeset;

            if (batchChangeset != null)
            {
                this.VisitBatchChangeset(batchChangeset);
                return;
            }

            ODataBatchRequestOperation batchRequestOperation = objectModelItem as ODataBatchRequestOperation;

            if (batchRequestOperation != null)
            {
                this.VisitBatchRequestOperation(batchRequestOperation);
                return;
            }

            ODataBatchResponseOperation batchResponseOperation = objectModelItem as ODataBatchResponseOperation;

            if (batchResponseOperation != null)
            {
                this.VisitBatchResponseOperation(batchResponseOperation);
                return;
            }

            if (objectModelItem == null || objectModelItem is ODataPrimitiveValue || objectModelItem.GetType().IsValueType || objectModelItem is string ||
                objectModelItem is byte[] || objectModelItem is ISpatial)
            {
                this.VisitPrimitiveValue(objectModelItem);
                return;
            }

            if (objectModelItem is ODataUntypedValue)
            {
                this.VisitPrimitiveValue(ParseJsonToPrimitiveValue((objectModelItem as ODataUntypedValue).RawValue));
                return;
            }

            this.VisitUnsupportedValue(objectModelItem);
        }
        private void VerifyTypeCollectionRoundtrip(ODataCollectionValue value, string propertyName, ODataFeedAndEntrySerializationInfo info)
        {
            var properties = new[] { new ODataProperty { Name = propertyName, Value = value } };
            var entry = new ODataEntry() { TypeName = "NS.Student", Properties = properties, SerializationInfo = info};
            MemoryStream stream = new MemoryStream();
            using (ODataAtomOutputContext outputContext = new ODataAtomOutputContext(
                ODataFormat.Atom,
                new NonDisposingStream(stream),
                Encoding.UTF8,
                new ODataMessageWriterSettings() { Version = ODataVersion.V4 },
                /*writingResponse*/ true,
                /*synchronous*/ true,
                model,
                /*urlResolver*/ null))
            {
                outputContext.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);
                var atomWriter = new ODataAtomWriter(outputContext, /*entitySet*/ null, /*entityType*/ null, /*writingFeed*/ false);
                atomWriter.WriteStart(entry);
                atomWriter.WriteEnd();

            }

            stream.Position = 0;
            object actualValue = null;

            using (ODataAtomInputContext inputContext = new ODataAtomInputContext(
                ODataFormat.Atom,
                stream,
                Encoding.UTF8,
                new ODataMessageReaderSettings(),
                /*readingResponse*/ true,
                /*synchronous*/ true,
                model,
                /*urlResolver*/ null))
            {
                var atomReader = new ODataAtomReader(inputContext, /*entitySet*/ null, /*entityType*/ null, /*writingFeed*/ false);
                while (atomReader.Read())
                {
                    if (atomReader.State == ODataReaderState.EntryEnd)
                    {
                        ODataEntry entryOut = atomReader.Item as ODataEntry;
                        actualValue = entryOut.Properties.Single(p => p.Name == propertyName).ODataValue;
                    }
                }
            }

            TestUtils.AssertODataValueAreEqual(actualValue as ODataValue, value);
        }
        public void ReadComplexValue_CanReadDynamicCollectionPropertiesForOpenComplexType()
        {
            // Arrange
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

            builder.ComplexType <SimpleOpenAddress>();
            builder.EnumType <SimpleEnum>();
            IEdmModel model = builder.GetEdmModel();
            IEdmComplexTypeReference addressTypeReference =
                model.GetEdmTypeReference(typeof(SimpleOpenAddress)).AsComplex();

            var deserializerProvider = new DefaultODataDeserializerProvider();
            var deserializer         = new ODataComplexTypeDeserializer(deserializerProvider);

            ODataEnumValue enumValue = new ODataEnumValue("Third", typeof(SimpleEnum).FullName);

            ODataCollectionValue collectionValue = new ODataCollectionValue
            {
                TypeName = "Collection(" + typeof(SimpleEnum).FullName + ")",
                Items    = new[] { enumValue, enumValue }
            };

            ODataComplexValue complexValue = new ODataComplexValue
            {
                Properties = new[]
                {
                    // declared properties
                    new ODataProperty {
                        Name = "Street", Value = "My Way #599"
                    },

                    // dynamic properties
                    new ODataProperty {
                        Name = "CollectionProperty", Value = collectionValue
                    }
                },
                TypeName = typeof(SimpleOpenAddress).FullName
            };

            ODataDeserializerContext readContext = new ODataDeserializerContext()
            {
                Model = model
            };

            // Act
            SimpleOpenAddress address = deserializer.ReadComplexValue(complexValue, addressTypeReference, readContext)
                                        as SimpleOpenAddress;

            // Assert
            Assert.NotNull(address);

            // Verify the declared properties
            Assert.Equal("My Way #599", address.Street);
            Assert.Null(address.City);

            // Verify the dynamic properties
            Assert.NotNull(address.Properties);
            Assert.Equal(1, address.Properties.Count());

            var collectionValues = Assert.IsType <List <SimpleEnum> >(address.Properties["CollectionProperty"]);

            Assert.NotNull(collectionValues);
            Assert.Equal(2, collectionValues.Count());
            Assert.Equal(SimpleEnum.Third, collectionValues[0]);
            Assert.Equal(SimpleEnum.Third, collectionValues[1]);
        }
示例#28
0
        /// <summary>
        /// Construct the default expected Json object with all metadata
        /// </summary>
        /// <param name="entityType">The IEdmEntityType</param>
        /// <param name="relativeEditLink">The relative edit link</param>
        /// <param name="entry">The ODataEntry </param>
        /// <param name="hasModel">Whether IEdmModel is provided to writer</param>
        /// <param name="isDerivedType">Whether the entry is of derived type</param>
        /// <returns>The expected Json object</returns>
        internal static Dictionary <string, object> ComputeExpectedFullMetadataEntryObject(IEdmEntityType entityType, string relativeEditLink, ODataResource entry, bool hasModel, bool isDerivedType = false)
        {
            var derivedTypeNameSegment = string.Empty;

            if (isDerivedType)
            {
                derivedTypeNameSegment = "/" + NameSpace + entityType.Name;
            }

            Dictionary <string, object> expectedEntryObject = new Dictionary <string, object>();

            expectedEntryObject.Add(JsonLightConstants.ODataTypeAnnotationName, "#" + NameSpace + entityType.Name);
            expectedEntryObject.Add(JsonLightConstants.ODataIdAnnotationName, entry.Id == null ? relativeEditLink : entry.Id.OriginalString);
            expectedEntryObject.Add(JsonLightConstants.ODataEditLinkAnnotationName, entry.EditLink == null ? relativeEditLink + derivedTypeNameSegment : entry.EditLink.AbsoluteUri);

            // when the writer has IEdmModel, expect other metadata in addition to id/edit/readlink
            if (hasModel)
            {
                // add expected actions
                var boundedActions = Model.FindDeclaredBoundOperations(entityType);
                foreach (var action in boundedActions)
                {
                    var actionFullName  = action.FullName();
                    var bindingTypeName = isDerivedType ? "/" + action.Parameters.First().Type.FullName() : "";
                    Dictionary <string, object> actionObject = new Dictionary <string, object>
                    {
                        { "title", actionFullName },
                        { "target", ServiceUri + relativeEditLink + bindingTypeName + "/" + actionFullName },
                    };
                    expectedEntryObject.Add("#" + actionFullName, actionObject);
                }

                var baseTypeToAddItem = entityType;
                while (baseTypeToAddItem != null)
                {
                    // add expected navigation properties
                    foreach (var navigation in baseTypeToAddItem.DeclaredNavigationProperties())
                    {
                        var navigationLinkKey = navigation.Name + JsonLightConstants.ODataPropertyAnnotationSeparator + JsonLightConstants.ODataNavigationLinkUrlAnnotationName;
                        if (!expectedEntryObject.ContainsKey(navigationLinkKey))
                        {
                            expectedEntryObject.Add(navigationLinkKey, string.Concat(ServiceUri, relativeEditLink, derivedTypeNameSegment, "/", navigation.Name));
                        }
                    }
                    baseTypeToAddItem = baseTypeToAddItem.BaseEntityType();
                }
            }

            foreach (var property in entry.Properties)
            {
                ODataCollectionValue collectionValue = property.Value as ODataCollectionValue;
                if (collectionValue != null)
                {
                    expectedEntryObject.Add(property.Name + JsonLightConstants.ODataTypeAnnotationName, "#" + collectionValue.TypeName);
                }

                expectedEntryObject.Add(property.Name, property.Value);
            }

            return(expectedEntryObject);
        }
        /// <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.IsNonEntityCollectionType(),
                "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.ODataMetadataNamespace))
                        {
                            if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
                            {
                                this.XmlReader.Skip();
                            }
                            else
                            {
                                object itemValue = this.ReadNonEntityValueImplementation(
                                    itemTypeReference,
                                    duplicatePropertyNamesChecker,
                                    collectionValidator,
                                    /*validateNullValue*/ true,
                                    /*propertyName*/ null);

                                // 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, itemTypeReference.IsNullable());

                                // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                                // the expected type (the itemType).
                                items.Add(itemValue);
                            }
                        }
                        else if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataMetadataNamespace));
                        }
                        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);
        }
示例#30
0
        public virtual void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, IEdmTypeReference valueTypeReference, bool isTopLevelProperty, bool isInUri, bool isOpenPropertyType)
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");
            Debug.Assert(!isTopLevelProperty || !isInUri, "Cannot be a top level property and in a uri");

            this.IncreaseRecursionDepth();

            // If the CollectionValue has type information write out the metadata and the type in it.
            string typeName = collectionValue.TypeName;

            if (isTopLevelProperty)
            {
                Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties.");
                if (typeName == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnCollection);
                }
            }
            else
            {
                // In requests, we allow the metadata type reference to be null if the type name is specified in the OM
                if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel())
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForCollectionValueInRequest);
                }
            }

            if (valueTypeReference == null)
            {
                valueTypeReference = TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, metadataTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator);
            }

            bool useValueProperty = false;

            if (isInUri)
            {
                // resolve the type name to the type; if no type name is specified we will use the
                // type inferred from metadata
                typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, metadataTypeReference, valueTypeReference, isOpenPropertyType);
                if (!string.IsNullOrEmpty(typeName))
                {
                    useValueProperty = true;

                    // "{"
                    this.JsonWriter.StartObjectScope();
                    this.ODataAnnotationWriter.WriteODataTypeInstanceAnnotation(typeName);
                    this.JsonWriter.WriteValuePropertyName();
                }
            }

            // [
            // This represents the array of items in the CollectionValue
            this.JsonWriter.StartArrayScope();

            // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration)
            IEnumerable items = collectionValue.Items;

            if (items != null)
            {
                IEdmTypeReference expectedItemTypeReference = valueTypeReference == null ? null : ((IEdmCollectionTypeReference)valueTypeReference).ElementType();

                IDuplicatePropertyNameChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable());

                    ODataResourceValue itemAsResourceValue = item as ODataResourceValue;
                    if (itemAsResourceValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNameChecker();
                        }

                        this.WriteResourceValue(
                            itemAsResourceValue,
                            expectedItemTypeReference,
                            false /*isOpenPropertyType*/,
                            duplicatePropertyNamesChecker);

                        duplicatePropertyNamesChecker.Reset();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        // by design: collection element's type name is not written for enum or non-spatial primitive value even in case of full metadata.
                        // because enum and non-spatial primitive types don't have inheritance, the type of each element is the same as the item type of the collection, whose type name for spatial types in full metadata mode.
                        ODataEnumValue enumValue = item as ODataEnumValue;
                        if (enumValue != null)
                        {
                            this.WriteEnumValue(enumValue, expectedItemTypeReference);
                        }
                        else
                        {
                            ODataUntypedValue untypedValue = item as ODataUntypedValue;
                            if (untypedValue != null)
                            {
                                this.WriteUntypedValue(untypedValue);
                            }
                            else if (item != null)
                            {
                                this.WritePrimitiveValue(item, expectedItemTypeReference);
                            }
                            else
                            {
                                this.WriteNullValue();
                            }
                        }
                    }
                }
            }

            // End the array scope which holds the items
            this.JsonWriter.EndArrayScope();

            if (useValueProperty)
            {
                this.JsonWriter.EndObjectScope();
            }

            this.DecreaseRecursionDepth();
        }
        /// <summary>
        /// Adds a data value to the dynamic properties dictionary (where it exists) on the specified <paramref name="instance"/>
        /// </summary>
        /// <param name="property">Property containing unmaterialzed value to apply</param>
        /// <param name="instance">Instance that may optionally contain the dynamic properties dictionary</param>
        internal void MaterializeDynamicProperty(ODataProperty property, object instance)
        {
            Debug.Assert(property != null, "property != null");
            Debug.Assert(instance != null, "instance != null");

            IDictionary <string, object> containerProperty;

            // Stop if owning type is not an open type
            // Or container property is not found
            // Or key with matching name already exists in the dictionary
            if (!ClientTypeUtil.IsInstanceOfOpenType(instance, this.MaterializerContext.Model) ||
                !ClientTypeUtil.TryGetContainerProperty(instance, out containerProperty) ||
                containerProperty.ContainsKey(property.Name))
            {
                return;
            }

            object value = property.Value;

            // Handles properties of known types returned with type annotations
            if (!(value is ODataValue) && PrimitiveType.IsKnownType(value.GetType()))
            {
                containerProperty.Add(property.Name, value);
                return;
            }

            // Handle untyped value
            ODataUntypedValue untypedVal = value as ODataUntypedValue;

            if (untypedVal != null)
            {
                value = CommonUtil.ParseJsonToPrimitiveValue(untypedVal.RawValue);
                containerProperty.Add(property.Name, value);
                return;
            }

            // Handle enum value
            ODataEnumValue enumVal = value as ODataEnumValue;

            if (enumVal != null)
            {
                Type clientType = ResolveClientTypeForDynamicProperty(enumVal.TypeName, instance);
                // Unable to resolve type for dynamic property
                if (clientType == null)
                {
                    return;
                }

                object materializedEnumValue;
                if (EnumValueMaterializationPolicy.TryMaterializeODataEnumValue(clientType, enumVal, out materializedEnumValue))
                {
                    containerProperty.Add(property.Name, materializedEnumValue);
                }

                return;
            }

            // Handle collection
            ODataCollectionValue collectionVal = value as ODataCollectionValue;

            if (collectionVal != null)
            {
                string collectionItemTypeName = CommonUtil.GetCollectionItemTypeName(collectionVal.TypeName, false);

                // Highly unlikely, but the method return null if the typeName argument does not meet certain expectations
                if (collectionItemTypeName == null)
                {
                    return;
                }

                Type collectionItemType;

                // ToNamedType will return true for primitive types
                if (!ClientConvert.ToNamedType(collectionItemTypeName, out collectionItemType))
                {
                    // Non-primitive collection
                    collectionItemType = ResolveClientTypeForDynamicProperty(collectionItemTypeName, instance);
                }

                if (collectionItemType == null)
                {
                    return;
                }

                object collectionInstance;
                if (this.CollectionValueMaterializationPolicy.TryMaterializeODataCollectionValue(collectionItemType, property, out collectionInstance))
                {
                    containerProperty.Add(property.Name, collectionInstance);
                }

                return;
            }
        }
        public void WritingNullableCollectionValue()
        {
            EdmModel model = new EdmModel();
            EdmEntityType entityType = new EdmEntityType("NS", "MyTestEntity");
            EdmStructuralProperty key =
                entityType.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32, false);
            entityType.AddKeys(key);
            entityType.AddStructuralProperty(
                "NullableIntNumbers",
                new EdmCollectionTypeReference(new EdmCollectionType(EdmCoreModel.Instance.GetInt32(true))));
            model.AddElement(entityType);

            var collectionValue = new ODataCollectionValue()
            {
                Items = new int?[] { null },
                TypeName = "Collection(Edm.Int32)"
            };

            ODataEntry entry = new ODataEntry()
            {
                Id = new Uri("http://test/Id"),
                TypeName = "NS.MyTestEntity",
                Properties = new[]
                {
                    new ODataProperty { Name = "NullableIntNumbers", Value = collectionValue },
                },
                SerializationInfo = MySerializationInfo
            };

            string outputPayload = this.WriterEntry(model, entry, entityType);
            string expectedPayload = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" " +
                    "xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" xmlns:georss=\"http://www.georss.org/georss\" " +
                    "xmlns:gml=\"http://www.opengis.net/gml\" " +
                    "m:context=\"http://odata.org/$metadata#MyTestEntity/$entity\">" +
                    "<id>http://test/Id</id>" +
                    "<category term=\"#NS.MyTestEntity\" scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" />" +
                    "<title />" +
                    "<author><name /></author>" +
                    "<content type=\"application/xml\">" +
                        "<m:properties>" +
                             "<d:NullableIntNumbers m:type=\"#Collection(Int32)\">" +
                                "<m:element m:null=\"true\" />" +
                             "</d:NullableIntNumbers>" +
                        "</m:properties>" +
                    "</content>" +
                "</entry>";

            outputPayload = Regex.Replace(outputPayload, "<updated>[^<]*</updated>", "");
            outputPayload.Should().Be(expectedPayload);
        }
示例#33
0
        private static void AssertODataCollectionValueAreEqual(ODataCollectionValue expectedCollectionValue, ODataCollectionValue actualCollectionValue)
        {
            Assert.IsNotNull(expectedCollectionValue);
            Assert.IsNotNull(actualCollectionValue);
            Assert.AreEqual(expectedCollectionValue.TypeName, actualCollectionValue.TypeName);
            var expectedItemsArray = expectedCollectionValue.Items.OfType <object>().ToArray();
            var actualItemsArray   = actualCollectionValue.Items.OfType <object>().ToArray();

            Assert.AreEqual(expectedItemsArray.Length, actualItemsArray.Length);
            for (int i = 0; i < expectedItemsArray.Length; i++)
            {
                var expectedOdataValue = expectedItemsArray[i] as ODataValue;
                var actualOdataValue   = actualItemsArray[i] as ODataValue;
                if (expectedOdataValue != null && actualOdataValue != null)
                {
                    AssertODataValueAreEqual(expectedOdataValue, actualOdataValue);
                }
                else
                {
                    Assert.AreEqual(expectedItemsArray[i], actualItemsArray[i]);
                }
            }
        }
 public void ReadInstanceAnnotationValueShouldReadCollectionOfComplexValues()
 {
     var deserializer = this.CreateJsonLightEntryAndFeedDeserializer("{\"@custom.complexCollectionAnnotation\":[{\"StringProperty\":\"StringValue\"}]}");
     AdvanceReaderToFirstPropertyValue(deserializer.JsonReader);
     var duplicatePropertyNamesChecker = new DuplicatePropertyNamesChecker(false /*allowDuplicateProperties*/, true /*isResponse*/);
     duplicatePropertyNamesChecker.AddODataPropertyAnnotation("custom.complexCollectionAnnotation", "odata.type", "Collection(TestNamespace.TestComplexType)");
     ODataCollectionValue value = (ODataCollectionValue)deserializer.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, "custom.complexCollectionAnnotation");
     var odataComplexValue = new ODataComplexValue { TypeName = "TestNamespace.TestComplexType", Properties = new[] { new ODataProperty { Name = "StringProperty", Value = "StringValue" } } };
     var odataCollectionValue = new ODataCollectionValue { TypeName = "Collection(TestNamespace.TestComplexType)", Items = new[] { odataComplexValue } };
     TestUtils.AssertODataValueAreEqual(value, odataCollectionValue);
 }
        public void ShouldWriteIndividualPropertyContextUriForCollectionType()
        {
            ODataValue value = new ODataCollectionValue();

            this.CreateIndividualPropertyContextUri(value, "Cities(9)/Restaurants").OriginalString.Should().Be(BuildExpectedContextUri("#Cities(9)/Restaurants"));
        }
 public void WritingAPrimitiveCollectionValuedAnnotationWithMetadataShouldWork()
 {
     ODataCollectionValue collection = new ODataCollectionValue { Items = new[] { 123 } };
     var annotation = AtomInstanceAnnotation.CreateFrom(new ODataInstanceAnnotation("custom.primitiveCollection", collection), /*target*/ null);
     this.serializer.WriteInstanceAnnotation(annotation);
     this.ValidatePayload("<annotation term=\"custom.primitiveCollection\" m:type=\"#Collection(Int32)\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" xmlns=\"http://docs.oasis-open.org/odata/ns/metadata\"><m:element>123</m:element></annotation>");
 }
示例#37
0
        /// <summary>
        /// Asynchronously reads the next node from the <paramref name="jsonReader"/> as an <see cref="ODataValue"/>.
        /// </summary>
        /// <param name="jsonReader">The reader to inspect.</param>
        /// <returns>A task that represents the asynchronous read operation.
        /// The value of the TResult parameter contains the <see cref="ODataValue"/> value read from the reader.</returns>
        internal static async Task <ODataValue> ReadODataValueAsync(this IJsonReaderAsync jsonReader)
        {
            if (jsonReader.NodeType == JsonNodeType.PrimitiveValue)
            {
                object primitiveValue = await jsonReader.ReadPrimitiveValueAsync()
                                        .ConfigureAwait(false);

                return(primitiveValue.ToODataValue());
            }
            else if (jsonReader.NodeType == JsonNodeType.StartObject)
            {
                await jsonReader.ReadStartObjectAsync()
                .ConfigureAwait(false);

                ODataResourceValue   resourceValue = new ODataResourceValue();
                List <ODataProperty> properties    = new List <ODataProperty>();

                while (jsonReader.NodeType != JsonNodeType.EndObject)
                {
                    ODataProperty property = new ODataProperty();
                    property.Name = await jsonReader.ReadPropertyNameAsync()
                                    .ConfigureAwait(false);

                    property.Value = await jsonReader.ReadODataValueAsync()
                                     .ConfigureAwait(false);

                    properties.Add(property);
                }

                resourceValue.Properties = properties;

                await jsonReader.ReadEndObjectAsync()
                .ConfigureAwait(false);

                return(resourceValue);
            }
            else if (jsonReader.NodeType == JsonNodeType.StartArray)
            {
                await jsonReader.ReadStartArrayAsync()
                .ConfigureAwait(false);

                ODataCollectionValue collectionValue = new ODataCollectionValue();
                List <object>        properties      = new List <object>();

                while (jsonReader.NodeType != JsonNodeType.EndArray)
                {
                    ODataValue odataValue = await jsonReader.ReadODataValueAsync()
                                            .ConfigureAwait(false);

                    properties.Add(odataValue);
                }

                collectionValue.Items = properties;
                await jsonReader.ReadEndArrayAsync()
                .ConfigureAwait(false);

                return(collectionValue);
            }
            else
            {
                return(await jsonReader.ReadAsUntypedOrNullValueAsync()
                       .ConfigureAwait(false));
            }
        }
 public void WritingAPrimitiveCollectionValuedAnnotationWithMismatchedMetadataShouldThrow()
 {
     ODataCollectionValue collection = new ODataCollectionValue { Items = new[] { 123.4 } };
     var annotation = AtomInstanceAnnotation.CreateFrom(new ODataInstanceAnnotation("custom.primitiveCollection", collection), /*target*/ null);
     Action test = () => this.serializer.WriteInstanceAnnotation(annotation);
     test.ShouldThrow<ODataException>().WithMessage(Strings.CollectionWithoutExpectedTypeValidator_IncompatibleItemTypeName("Edm.Double", "Edm.Int32"));
 }
示例#39
0
        internal static object ConvertAnnotationValue(object oDataValue, ref IEdmTypeReference propertyType, ODataDeserializerProvider deserializerProvider,
                                                      ODataDeserializerContext readContext)
        {
            if (oDataValue == null)
            {
                return(null);
            }

            ODataEnumValue enumValue = oDataValue as ODataEnumValue;

            if (enumValue != null)
            {
                return(ConvertEnumValue(enumValue, ref propertyType, deserializerProvider, readContext));
            }

            ODataCollectionValue collection = oDataValue as ODataCollectionValue;

            if (collection != null)
            {
                return(ConvertCollectionValue(collection, ref propertyType, deserializerProvider, readContext));
            }

            ODataUntypedValue untypedValue = oDataValue as ODataUntypedValue;

            if (untypedValue != null)
            {
                Contract.Assert(!String.IsNullOrEmpty(untypedValue.RawValue));

                if (untypedValue.RawValue.StartsWith("[", StringComparison.Ordinal) ||
                    untypedValue.RawValue.StartsWith("{", StringComparison.Ordinal))
                {
                    throw new ODataException(Error.Format(SRResources.InvalidODataUntypedValue, untypedValue.RawValue));
                }

                oDataValue = ConvertPrimitiveValue(untypedValue.RawValue);
            }

            ODataResourceValue annotationVal = oDataValue as ODataResourceValue;

            if (annotationVal != null)
            {
                var annotationType = readContext.Model.FindDeclaredType(annotationVal.TypeName).ToEdmTypeReference(true) as IEdmStructuredTypeReference;

                ODataResourceDeserializer deserializer = new ODataResourceDeserializer(deserializerProvider);

                object resource = deserializer.CreateResourceInstance(annotationType, readContext);

                if (resource != null)
                {
                    foreach (var prop in annotationVal.Properties)
                    {
                        deserializer.ApplyStructuralProperty(resource, prop, annotationType, readContext);
                    }
                }

                return(resource);
            }

            ODataPrimitiveValue primitiveValue = oDataValue as ODataPrimitiveValue;

            if (primitiveValue != null)
            {
                return(EdmPrimitiveHelpers.ConvertPrimitiveValue(primitiveValue.Value, primitiveValue.Value.GetType()));
            }

            return(oDataValue);
        }
 public void BuildPropertyContextUriForCollectionPropertyValue()
 {
     ODataCollectionValue value = new ODataCollectionValue { TypeName = "FQNS.FakeType" };
     var contextUri = this.CreatePropertyContextUri(value);
     contextUri.OriginalString.Should().Be(BuildExpectedContextUri("#FQNS.FakeType"));
 }
示例#41
0
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool allowStreamProperty,
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            ProjectedPropertiesAnnotation projectedProperties)
        {
            WriterValidationUtils.ValidatePropertyNotNull(property);

            string propertyName = property.Name;

            if (projectedProperties.ShouldSkipProperty(propertyName))
            {
                return;
            }

            WriterValidationUtils.ValidatePropertyName(propertyName, bypassValidation);
            duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property);

            if (property.InstanceAnnotations.Any())
            {
                if (isTopLevel)
                {
                    this.InstanceAnnotationWriter.WriteInstanceAnnotations(property.InstanceAnnotations);
                }
                else
                {
                    this.InstanceAnnotationWriter.WriteInstanceAnnotations(property.InstanceAnnotations, propertyName);
                }
            }

            IEdmProperty edmProperty = WriterValidationUtils.ValidatePropertyDefined(
                propertyName,
                owningType,
                !this.bypassValidation);
            IEdmTypeReference propertyTypeReference = edmProperty == null ? null : edmProperty.Type;

            ODataValue value = property.ODataValue;

            ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;

            if (streamReferenceValue != null)
            {
                if (!allowStreamProperty)
                {
                    throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }

                Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                Debug.Assert(!isTopLevel, "Stream properties are not allowed at the top level.");
                WriterValidationUtils.ValidateStreamReferenceProperty(property, edmProperty, this.WritingResponse, this.bypassValidation);
                this.WriteStreamReferenceProperty(propertyName, streamReferenceValue);
                return;
            }

            string wirePropertyName = isTopLevel ? JsonLightConstants.ODataValuePropertyName : propertyName;

            if (value is ODataNullValue || value == null)
            {
                WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.MessageWriterSettings.WriterBehavior, this.Model, this.bypassValidation);

                if (isTopLevel)
                {
                    // Write the special null marker for top-level null properties.
                    this.ODataAnnotationWriter.WriteInstanceAnnotationName(ODataAnnotationNames.ODataNull);
                    this.JsonWriter.WriteValue(true);
                }
                else
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                    this.JsonLightValueSerializer.WriteNullValue();
                }

                return;
            }

            bool isOpenPropertyType = this.IsOpenProperty(property, owningType, edmProperty);

            if (isOpenPropertyType && this.JsonLightOutputContext.MessageWriterSettings.EnableFullValidation)
            {
                ValidationUtils.ValidateOpenPropertyValue(propertyName, value);
            }

            ODataComplexValue complexValue = value as ODataComplexValue;

            if (complexValue != null)
            {
                if (!isTopLevel)
                {
                    this.JsonWriter.WriteName(wirePropertyName);
                }

                this.JsonLightValueSerializer.WriteComplexValue(complexValue, propertyTypeReference, isTopLevel, isOpenPropertyType, this.CreateDuplicatePropertyNamesChecker());
                return;
            }

            IEdmTypeReference typeFromValue = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, value, isOpenPropertyType);
            ODataEnumValue    enumValue     = value as ODataEnumValue;

            if (enumValue != null)
            {
                // This is a work around, needTypeOnWire always = true for client side:
                // ClientEdmModel's reflection can't know a property is open type even if it is, so here
                // make client side always write 'odata.type' for enum.
                bool   needTypeOnWire  = string.Equals(this.JsonLightOutputContext.Model.GetType().Name, "ClientEdmModel", StringComparison.OrdinalIgnoreCase);
                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(
                    enumValue, propertyTypeReference, typeFromValue, needTypeOnWire ? true /* leverage this flag to write 'odata.type' */ : isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);
                this.JsonWriter.WriteName(wirePropertyName);
                this.JsonLightValueSerializer.WriteEnumValue(enumValue, propertyTypeReference);
                return;
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                string collectionTypeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, collectionTypeNameToWrite, isTopLevel);
                this.JsonWriter.WriteName(wirePropertyName);

                // passing false for 'isTopLevel' because the outer wrapping object has already been written.
                this.JsonLightValueSerializer.WriteCollectionValue(collectionValue, propertyTypeReference, isTopLevel, false /*isInUri*/, isOpenPropertyType);
            }
            else
            {
                ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue;
                Debug.Assert(primitiveValue != null, "primitiveValue != null");

                string typeNameToWrite = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(primitiveValue, propertyTypeReference, typeFromValue, isOpenPropertyType);
                this.WritePropertyTypeName(wirePropertyName, typeNameToWrite, isTopLevel);

                this.JsonWriter.WriteName(wirePropertyName);
                this.JsonLightValueSerializer.WritePrimitiveValue(primitiveValue.Value, propertyTypeReference);
            }
        }
 public void ShouldWriteIndividualPropertyContextUriForCollectionType()
 {
     ODataValue value = new ODataCollectionValue();
     this.CreateIndividualPropertyContextUri(value, "Cities(9)/Restaurants").OriginalString.Should().Be(BuildExpectedContextUri("#Cities(9)/Restaurants"));
 }
        private async Task WriteNestedCollectionAsync(ODataWriter entryWriter, string entryName, ODataCollectionValue collection)
        {
            await entryWriter.WriteStartAsync(new ODataNestedResourceInfo()
            {
                Name         = entryName,
                IsCollection = true,
            }).ConfigureAwait(false);

            await entryWriter.WriteStartAsync(new ODataResourceSet());

            foreach (var item in collection.Items)
            {
                await WriteEntryPropertiesAsync(entryWriter, item as ODataResource, null);
            }
            await entryWriter.WriteEndAsync().ConfigureAwait(false);

            await entryWriter.WriteEndAsync().ConfigureAwait(false);
        }
        public void WriteTopLevelErrorWithCollectionOfComplexInstanceAnnotationWithNoTypeNameShouldThrow()
        {
            SetupSerializerAndRunTest(null, serializer =>
            {
                ODataError error = new ODataError();
                var instanceAnnotations = new Collection<ODataInstanceAnnotation>();
                var collection = new ODataCollectionValue
                {
                    Items = new[] { new ODataComplexValue(), new ODataComplexValue { TypeName = "ns.ErrorDetails" } }
                };
                ODataInstanceAnnotation annotation = new ODataInstanceAnnotation("sample.collection", collection);
                instanceAnnotations.Add(annotation);
                error.InstanceAnnotations = instanceAnnotations;

                Action writeError = () => serializer.WriteTopLevelError(error, false);
                writeError.ShouldThrow<ODataException>().WithMessage(Strings.WriterValidationUtils_MissingTypeNameWithMetadata);
            });
        }
        public void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isTopLevelProperty, bool isInUri, bool isOpenPropertyType)
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");
            Debug.Assert(!isTopLevelProperty || !isInUri, "Cannot be a top level property and in a uri");

            this.IncreaseRecursionDepth();

            // If the CollectionValue has type information write out the metadata and the type in it.
            string typeName = collectionValue.TypeName;

            if (isTopLevelProperty)
            {
                Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties.");
                if (typeName == null)
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnCollection);
                }
            }
            else
            {
                // In requests, we allow the metadata type reference to be null if the type name is specified in the OM
                if (metadataTypeReference == null && !this.WritingResponse && typeName == null && this.Model.IsUserModel())
                {
                    throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueSerializer_NoExpectedTypeOrTypeNameSpecifiedForCollectionValueInRequest);
                }
            }

            // resolve the type name to the type; if no type name is specified we will use the 
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, collectionValue, isOpenPropertyType);
            typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, metadataTypeReference, collectionTypeReference, isOpenPropertyType);
            bool useValueProperty = isInUri && !string.IsNullOrEmpty(typeName);
            if (useValueProperty)
            {
                // "{"
                this.JsonWriter.StartObjectScope();
                this.ODataAnnotationWriter.WriteODataTypeInstanceAnnotation(typeName);
                this.JsonWriter.WriteValuePropertyName();
            }

            // [
            // This represents the array of items in the CollectionValue
            this.JsonWriter.StartArrayScope();

            // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration)
            IEnumerable items = collectionValue.Items;
            if (items != null)
            {
                IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable());

                    ODataComplexValue itemAsComplexValue = item as ODataComplexValue;
                    if (itemAsComplexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            itemAsComplexValue,
                            expectedItemTypeReference,
                            false /*isTopLevel*/,
                            false /*isOpenPropertyType*/,
                            duplicatePropertyNamesChecker);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        // by design: collection element's type name is not written for enum or non-spatial primitive value even in case of full metadata.
                        // because enum and non-spatial primitive types don't have inheritance, the type of each element is the same as the item type of the collection, whose type name for spatial types in full metadata mode.
                        ODataEnumValue enumValue = item as ODataEnumValue;
                        if (enumValue != null)
                        {
                            this.WriteEnumValue(enumValue, expectedItemTypeReference);
                        }
                        else
                        {
                            ODataUntypedValue untypedValue = item as ODataUntypedValue;
                            if (untypedValue != null)
                            {
                                this.WriteUntypedValue(untypedValue);
                            }
                            else if (item != null)
                            {
                                this.WritePrimitiveValue(item, expectedItemTypeReference);
                            }
                            else
                            {
                                this.WriteNullValue();
                            }
                        }
                    }
                }
            }

            // End the array scope which holds the items
            this.JsonWriter.EndArrayScope();

            if (useValueProperty)
            {
                this.JsonWriter.EndObjectScope();
            }

            this.DecreaseRecursionDepth();
        }
示例#46
0
文件: EpmReader.cs 项目: tapika/swupd
        private void SetEpmValueForSegment(
            EntityPropertyMappingInfo epmInfo,
            int propertyValuePathIndex,
            IEdmStructuredTypeReference segmentStructuralTypeReference,
            ReadOnlyEnumerable <ODataProperty> existingProperties,
            object propertyValue)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(propertyValuePathIndex < epmInfo.PropertyValuePath.Length, "The propertyValuePathIndex is out of bounds.");
            Debug.Assert(existingProperties != null, "existingProperties != null");

            string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName;

            // Do not set out-of-content values if the EPM is defined as KeepInContent=true.
            if (epmInfo.Attribute.KeepInContent)
            {
                return;
            }

            // Try to find the property in the existing properties
            // If the property value is atomic from point of view of EPM (non-streaming collection or primitive) then if it already exists
            // it must have been in-content, and thus we leave it as is (note that two EPMs can't map to the same property, we verify that upfront).
            // If the property value is non-atomic, then it is a complex value, we might want to merge the new value comming from EPM with it.
            ODataProperty     existingProperty     = existingProperties.FirstOrDefault(p => string.CompareOrdinal(p.Name, propertyName) == 0);
            ODataComplexValue existingComplexValue = null;

            if (existingProperty != null)
            {
                // In case the property exists and it's a complex value we will try to merge.
                // Note that if the property is supposed to be complex, but it already has a null value, then the null wins.
                // Since in-content null complex value wins over any EPM complex value.
                existingComplexValue = existingProperty.Value as ODataComplexValue;
                if (existingComplexValue == null)
                {
                    return;
                }
            }

            IEdmProperty propertyMetadata = segmentStructuralTypeReference.FindProperty(propertyName);

            Debug.Assert(propertyMetadata != null || segmentStructuralTypeReference.IsOpen(), "We should have verified that if the property is not declared the type must be open.");

            // TODO: Server seems to have a bug where if there's an EPM for an open property where the EPM uses complex types (the source path is deeper than 1)
            // then it will actually materialize the property as entry level string property, with the value of the deep property value. If there are multiple deep
            // EPM for the same top-level open property it seems to set the entry level property multiple times with the values as they come from payload.
            // Client on the other hand doesn't have open properties, and always has a type, so no problem there.
            if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1)
            {
                throw new ODataException(ODataErrorStrings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath));
            }

            // Open properties in EPM are by default of type Edm.String - there's no way to specify a typename in EPM
            // consumer is free to do the conversion later on if it needs to.
            // Note that this effectively means that ODataMessageReaderSettings.DisablePrimitiveTypeConversion is as if it's turned on for open EPM properties.
            IEdmTypeReference propertyType;

            if (propertyMetadata == null ||
                (this.MessageReaderSettings.DisablePrimitiveTypeConversion && propertyMetadata.Type.IsODataPrimitiveTypeKind()))
            {
                propertyType = EdmCoreModel.Instance.GetString(/*nullable*/ true);
            }
            else
            {
                propertyType = propertyMetadata.Type;
            }

            // NOTE: WCF DS Server only applies the values when
            // - It's an open property
            // - It's not a key property
            // - It's a key property and it's a POST operation
            // ODataLib here will always set the property though.
            switch (propertyType.TypeKind())
            {
            case EdmTypeKind.Primitive:
            {
                if (propertyType.IsStream())
                {
                    throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty));
                }

                object primitiveValue;
                if (propertyValue == null)
                {
                    ReaderValidationUtils.ValidateNullValue(
                        this.atomInputContext.Model,
                        propertyType,
                        this.atomInputContext.MessageReaderSettings,
                        /*validateNullValue*/ true,
                        this.atomInputContext.Version,
                        propertyName);

                    primitiveValue = null;
                }
                else
                {
                    // Convert the value to the desired target type
                    primitiveValue = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, propertyType.AsPrimitive());
                }

                this.AddEpmPropertyValue(existingProperties, propertyName, primitiveValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
            }

            break;

            case EdmTypeKind.Complex:
                // Note: Unlike WCF DS we don't have a preexisting instance to override (since complex values are atomic, so we should not updated them)
                // In our case the complex value either doesn't exist yet on the entry being reported (easy, create it)
                // or it exists, but then it was created during reading of previous normal or EPM properties for this entry. It never exists before
                // we ever get to see the entity. So in our case we will never recreate the complex value, we always start with new one
                // and update it with new properties as they come. (Next time we will start over with a new complex value.)
                Debug.Assert(
                    existingComplexValue == null || (existingProperty != null && existingProperty.Value == existingComplexValue),
                    "If we have existing complex value, we must have an existing property as well.");
                Debug.Assert(
                    epmInfo.PropertyValuePath.Length > propertyValuePathIndex + 1,
                    "Complex value can not be a leaf segment in the source property path. We should have failed constructing the EPM trees for it.");

                if (existingComplexValue == null)
                {
                    Debug.Assert(existingProperty == null, "If we don't have an existing complex value, then we must not have an existing property at all.");

                    // Create a new complex value and set its type name to the type name of the property type (in case of EPM we never have type name from the payload)
                    existingComplexValue = new ODataComplexValue
                    {
                        TypeName   = propertyType.ODataFullName(),
                        Properties = new ReadOnlyEnumerable <ODataProperty>()
                    };

                    this.AddEpmPropertyValue(existingProperties, propertyName, existingComplexValue, segmentStructuralTypeReference.IsODataEntityTypeKind());
                }

                // Get the properties list of the complex value and recursively set the next EPM segment value to it.
                // Note that on inner complex value we don't need to check for duplicate properties
                // because EPM will never add a property which already exists (see the start of this method).
                IEdmComplexTypeReference complexPropertyTypeReference = propertyType.AsComplex();
                Debug.Assert(complexPropertyTypeReference != null, "complexPropertyTypeReference != null");
                this.SetEpmValueForSegment(
                    epmInfo,
                    propertyValuePathIndex + 1,
                    complexPropertyTypeReference,
                    existingComplexValue.Properties.ToReadOnlyEnumerable("Properties"),
                    propertyValue);

                break;

            case EdmTypeKind.Collection:
                Debug.Assert(propertyType.IsNonEntityCollectionType(), "Collection types in EPM must be atomic.");

                // In this case the property value is the internal list of items.
                // Create a new collection value and set the list as the list of items on it.
                ODataCollectionValue collectionValue = new ODataCollectionValue
                {
                    TypeName = propertyType.ODataFullName(),
                    Items    = new ReadOnlyEnumerable((List <object>)propertyValue)
                };

                this.AddEpmPropertyValue(existingProperties, propertyName, collectionValue, segmentStructuralTypeReference.IsODataEntityTypeKind());

                break;

            default:
                throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind));
            }
        }
        public override async Task <object> ReadAsync(ODataMessageReader messageReader, Type type, ODataDeserializerContext readContext)
        {
            if (messageReader == null)
            {
                throw Error.ArgumentNull("messageReader");
            }

            IEdmAction action = GetAction(readContext);

            Contract.Assert(action != null);

            // Create the correct resource type;
            Dictionary <string, object> payload;

            if (type == typeof(ODataActionParameters))
            {
                payload = new ODataActionParameters();
            }
            else
            {
                payload = new ODataUntypedActionParameters(action);
            }

            ODataParameterReader reader = await messageReader.CreateODataParameterReaderAsync(action).ConfigureAwait(false);

            while (await reader.ReadAsync().ConfigureAwait(false))
            {
                string parameterName             = null;
                IEdmOperationParameter parameter = null;

                switch (reader.State)
                {
                case ODataParameterReaderState.Value:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    // ODataLib protects against this but asserting just in case.
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    if (parameter.Type.IsPrimitive())
                    {
                        payload[parameterName] = reader.Value;
                    }
                    else
                    {
                        IODataEdmTypeDeserializer deserializer = DeserializerProvider.GetEdmTypeDeserializer(parameter.Type);
                        payload[parameterName] = deserializer.ReadInline(reader.Value, parameter.Type, readContext);
                    }
                    break;

                case ODataParameterReaderState.Collection:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    // ODataLib protects against this but asserting just in case.
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    IEdmCollectionTypeReference collectionType = parameter.Type as IEdmCollectionTypeReference;
                    Contract.Assert(collectionType != null);
                    ODataCollectionValue value = await ODataCollectionDeserializer
                                                 .ReadCollectionAsync(reader.CreateCollectionReader()).ConfigureAwait(false);

                    ODataCollectionDeserializer collectionDeserializer = (ODataCollectionDeserializer)DeserializerProvider.GetEdmTypeDeserializer(collectionType);
                    payload[parameterName] = collectionDeserializer.ReadInline(value, collectionType, readContext);
                    break;

                case ODataParameterReaderState.Resource:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    Contract.Assert(parameter.Type.IsStructured());

                    ODataReader resourceReader = reader.CreateResourceReader();
                    object      item           = await resourceReader.ReadResourceOrResourceSetAsync().ConfigureAwait(false);

                    ODataResourceDeserializer resourceDeserializer = (ODataResourceDeserializer)DeserializerProvider.GetEdmTypeDeserializer(parameter.Type);
                    payload[parameterName] = resourceDeserializer.ReadInline(item, parameter.Type, readContext);
                    break;

                case ODataParameterReaderState.ResourceSet:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));

                    IEdmCollectionTypeReference resourceSetType = parameter.Type as IEdmCollectionTypeReference;
                    Contract.Assert(resourceSetType != null);

                    ODataReader resourceSetReader = reader.CreateResourceSetReader();
                    object      feed = await resourceSetReader.ReadResourceOrResourceSetAsync().ConfigureAwait(false);

                    ODataResourceSetDeserializer resourceSetDeserializer = (ODataResourceSetDeserializer)DeserializerProvider.GetEdmTypeDeserializer(resourceSetType);

                    object result = resourceSetDeserializer.ReadInline(feed, resourceSetType, readContext);

                    IEdmTypeReference elementTypeReference = resourceSetType.ElementType();
                    Contract.Assert(elementTypeReference.IsStructured());

                    IEnumerable enumerable = result as IEnumerable;
                    if (enumerable != null)
                    {
                        if (readContext.IsNoClrType)
                        {
                            payload[parameterName] = enumerable.ConvertToEdmObject(resourceSetType);
                        }
                        else
                        {
                            Type        elementClrType = readContext.Model.GetClrType(elementTypeReference);
                            IEnumerable castedResult   =
                                _castMethodInfo.MakeGenericMethod(elementClrType)
                                .Invoke(null, new[] { result }) as IEnumerable;
                            payload[parameterName] = castedResult;
                        }
                    }
                    break;
                }
            }

            return(payload);
        }
 public void CollectionOfPrimitiveCustomInstanceAnnotationOnErrorShouldRoundtrip()
 {
     var originalCollectionValue = new ODataCollectionValue
     {
         TypeName = "Collection(Edm.String)",
         Items = new[] { "value1", "value2" }
     };
     var original = new KeyValuePair<string, ODataValue>("sample.error", originalCollectionValue);
     var error = this.WriteThenReadErrorWithInstanceAnnotation(original);
     var annotation = RunBasicVerificationAndGetAnnotationValue("sample.error", error);
     annotation.Should().BeOfType<ODataCollectionValue>();
     annotation.As<ODataCollectionValue>().Items.Cast<string>().Count().Should().Be(2);
 }
        /// <summary>
        /// Write the items of a collection in ATOM format.
        /// </summary>
        /// <param name="collectionValue">The collection value to write.</param>
        /// <param name="propertyTypeReference">The type reference of the collection value (or null if not metadata is available).</param>
        /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param>
        /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param>
        private void WriteCollectionValue(
            ODataCollectionValue collectionValue,
            IEdmTypeReference propertyTypeReference,
            bool isOpenPropertyType,
            bool isWritingCollection)
        {
            Debug.Assert(collectionValue != null, "collectionValue != null");

            this.IncreaseRecursionDepth();

            // resolve the type name to the type; if no type name is specified we will use the 
            // type inferred from metadata
            IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator);

            string collectionItemTypeName;
            string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation<SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName);

            if (typeName != null)
            {
                this.WritePropertyTypeAttribute(typeName);
            }

            // COMPAT 4: ATOM and JSON format for collections - The collection format is not formally specified yet, since it's a new feature
            // If the official format deviates from the current WCFDS behavior we would have to introduce a back compat mode here as well.
            IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType();

            CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName);

            IEnumerable items = collectionValue.Items;
            if (items != null)
            {
                DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null;
                foreach (object item in items)
                {
                    ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable());

                    this.XmlWriter.WriteStartElement(AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, AtomConstants.ODataMetadataNamespace);
                    ODataComplexValue complexValue = item as ODataComplexValue;
                    if (complexValue != null)
                    {
                        if (duplicatePropertyNamesChecker == null)
                        {
                            duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker();
                        }

                        this.WriteComplexValue(
                            complexValue,
                            expectedItemTypeReference,
                            false,
                            isWritingCollection,
                            null /* beforeValueAction */,
                            null /* afterValueAction */,
                            duplicatePropertyNamesChecker,
                            collectionValidator,
                            null /* projectedProperties */);

                        duplicatePropertyNamesChecker.Clear();
                    }
                    else
                    {
                        Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)");
                        Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)");

                        ODataEnumValue enumValue = item as ODataEnumValue;
                        if (enumValue != null)
                        {
                            // Note: Currently there is no way for a user to control enum type's serializationTypeNameAnnotation information when the enum values are part of a collection.
                            this.WriteEnumValue(enumValue, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null);
                        }
                        else
                        {
                            // Note: Currently there is no way for a user to control primitive type information when the primitive values are part of a collection.
                            if (item != null)
                            {
                                this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null);
                            }
                            else
                            {
                                this.WriteNullCollectionElementValue(expectedItemTypeReference);
                            }
                        }
                    }

                    this.XmlWriter.WriteEndElement();
                }
            }

            this.DecreaseRecursionDepth();
        }
        /// <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.IsNonEntityCollectionType(),
                "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();

                // If we don't have metadata (that is collectionType is null) we parse the type name
                // and extract the item type name from it. Then we create a CollectionWithoutExpectedTypeValidator
                // instance and use it to ensure that all item types read for the collection value are consistent with
                // the item type derived from the collection value's type name.
                // Note that if an item does not specify a type name but the collection value does, the item will be
                // assigned the item type name computed from the collection value's type.
                // Note that JSON doesn't have this problem since in JSON we always have metadata and thus the collectionType
                // is never null by the time we get to a similar place in the code.
                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.ODataMetadataNamespace))
                            {
                                if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName))
                                {
                                    this.XmlReader.Skip();
                                }
                                else
                                {
                                    object itemValue = this.ReadNonEntityValueImplementation(
                                        itemTypeReference,
                                        duplicatePropertyNamesChecker,
                                        collectionValidator,
                                        /*validateNullValue*/ true,
                                        /*propertyName*/ null);

                                    // 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, itemTypeReference.IsNullable());

                                    // Note that the ReadNonEntityValue already validated that the actual type of the value matches
                                    // the expected type (the itemType).
                                    items.Add(itemValue);
                                }
                            }
                            else if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace))
                            {
                                throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataMetadataNamespace));  
                            }
                            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;
        }
        private void WriteProperty(
            ODataProperty property,
            IEdmStructuredType owningType,
            bool isTopLevel,
            bool allowStreamProperty,
            IDuplicatePropertyNameChecker duplicatePropertyNameChecker)
        {
            WriterValidationUtils.ValidatePropertyNotNull(property);

            string propertyName = property.Name;

            ODataValue value = property.ODataValue;

            // If no validation is required, we don't need property serialization info and could try to write null property right away
            if (!this.MessageWriterSettings.ThrowIfTypeConflictsWithMetadata && this.MessageWriterSettings.IgnoreNullValues)
            {
                if (value is ODataNullValue || value == null)
                {
                    return;
                }
            }

            if (this.JsonLightOutputContext.MessageWriterSettings.Validations != ValidationKinds.None)
            {
                WriterValidationUtils.ValidatePropertyName(propertyName);
            }

            if (!this.JsonLightOutputContext.PropertyCacheHandler.InResourceSetScope())
            {
                this.currentPropertyInfo = new PropertySerializationInfo(propertyName, owningType)
                {
                    IsTopLevel = isTopLevel
                };
            }
            else
            {
                this.currentPropertyInfo = this.JsonLightOutputContext.PropertyCacheHandler.GetProperty(propertyName, owningType);
            }

            WriterValidationUtils.ValidatePropertyDefined(this.currentPropertyInfo, this.MessageWriterSettings.ThrowOnUndeclaredPropertyForNonOpenType);

            duplicatePropertyNameChecker.ValidatePropertyUniqueness(property);

            if (currentPropertyInfo.MetadataType.IsUndeclaredProperty)
            {
                WriteODataTypeAnnotation(property, isTopLevel);
            }

            WriteInstanceAnnotation(property, isTopLevel, currentPropertyInfo.MetadataType.IsUndeclaredProperty);

            // handle ODataUntypedValue
            ODataUntypedValue untypedValue = value as ODataUntypedValue;

            if (untypedValue != null)
            {
                WriteUntypedValue(untypedValue);
                return;
            }

            ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue;

            if (streamReferenceValue != null)
            {
                if (!allowStreamProperty)
                {
                    throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataResource(propertyName));
                }

                Debug.Assert(owningType == null || owningType.IsODataEntityTypeKind(), "The metadata should not allow named stream properties to be defined on a non-entity type.");
                Debug.Assert(!isTopLevel, "Stream properties are not allowed at the top level.");
                WriterValidationUtils.ValidateStreamReferenceProperty(property, currentPropertyInfo.MetadataType.EdmProperty, this.WritingResponse);
                this.WriteStreamReferenceProperty(propertyName, streamReferenceValue);
                return;
            }

            if (value is ODataNullValue || value == null)
            {
                this.WriteNullProperty(property);
                return;
            }

            bool isOpenPropertyType = this.IsOpenProperty(property);

            ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue;

            if (primitiveValue != null)
            {
                this.WritePrimitiveProperty(primitiveValue, isOpenPropertyType);
                return;
            }

            ODataEnumValue enumValue = value as ODataEnumValue;

            if (enumValue != null)
            {
                this.WriteEnumProperty(enumValue, isOpenPropertyType);
                return;
            }

            ODataCollectionValue collectionValue = value as ODataCollectionValue;

            if (collectionValue != null)
            {
                this.WriteCollectionProperty(collectionValue, isOpenPropertyType);
                return;
            }
        }
        public void WritingCollectionValueWithNullValueForNonNullableElementTypeShouldFail()
        {
            var serializer = CreateODataJsonLightValueSerializer(true);

            var collectionValue = new ODataCollectionValue()
            {
                Items = new int?[] { null },
                TypeName = "Collection(Edm.Int32)"
            };

            var stringCollectionTypeRef = EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetInt32(false));
            Action test = () => serializer.WriteCollectionValue(collectionValue, stringCollectionTypeRef, false, false, false);

            test.ShouldThrow<ODataException>().WithMessage(Strings.ValidationUtils_NonNullableCollectionElementsMustNotBeNull);
        }
        /// <inheritdoc />
        public override object Read(ODataMessageReader messageReader, Type type, ODataDeserializerContext readContext)
        {
            if (messageReader == null)
            {
                throw Error.ArgumentNull("messageReader");
            }

            IEdmAction action = GetAction(readContext);

            Contract.Assert(action != null);

            // Create the correct resource type;
            Dictionary <string, object> payload;

            if (type == typeof(ODataActionParameters))
            {
                payload = new ODataActionParameters();
            }
            else
            {
                payload = new ODataUntypedActionParameters(action);
            }

            ODataParameterReader reader = messageReader.CreateODataParameterReader(action);

            while (reader.Read())
            {
                string parameterName             = null;
                IEdmOperationParameter parameter = null;

                switch (reader.State)
                {
                case ODataParameterReaderState.Value:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    // ODataLib protects against this but asserting just in case.
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    if (parameter.Type.IsPrimitive())
                    {
                        payload[parameterName] = reader.Value;
                    }
                    else
                    {
                        ODataEdmTypeDeserializer deserializer = DeserializerProvider.GetEdmTypeDeserializer(parameter.Type);
                        payload[parameterName] = deserializer.ReadInline(reader.Value, parameter.Type, readContext);
                    }
                    break;

                case ODataParameterReaderState.Collection:
                    parameterName = reader.Name;
                    parameter     = action.Parameters.SingleOrDefault(p => p.Name == parameterName);
                    // ODataLib protects against this but asserting just in case.
                    Contract.Assert(parameter != null, String.Format(CultureInfo.InvariantCulture, "Parameter '{0}' not found.", parameterName));
                    IEdmCollectionTypeReference collectionType = parameter.Type as IEdmCollectionTypeReference;
                    Contract.Assert(collectionType != null);
                    ODataCollectionValue        value = ODataCollectionDeserializer.ReadCollection(reader.CreateCollectionReader());
                    ODataCollectionDeserializer collectionDeserializer = (ODataCollectionDeserializer)DeserializerProvider.GetEdmTypeDeserializer(collectionType);
                    payload[parameterName] = collectionDeserializer.ReadInline(value, collectionType, readContext);
                    break;

                default:
                    break;
                }
            }

            return(payload);
        }
示例#54
0
文件: EpmWriter.cs 项目: tapika/swupd
        /// <summary>
        /// Reads a property value starting with the specified index to the property value path.
        /// </summary>
        /// <param name="epmInfo">The EPM info which describes the mapping for which to read the property value.</param>
        /// <param name="cachedProperties">The enumeration of properties to search for the first property in the property value path.</param>
        /// <param name="sourceSegmentIndex">The index in the property value path to start with.</param>
        /// <param name="structuredTypeReference">The type of the entry or complex value the <paramref name="cachedProperties"/> enumeration belongs to.</param>
        /// <param name="epmValueCache">The EPM value cache to use.</param>
        /// <returns>The value of the property (may be null), or null if the property itself was not found due to one of its parent properties being null.</returns>
        private object ReadPropertyValue(
            EntityPropertyMappingInfo epmInfo,
            IEnumerable <ODataProperty> cachedProperties,
            int sourceSegmentIndex,
            IEdmStructuredTypeReference structuredTypeReference,
            EpmValueCache epmValueCache)
        {
            Debug.Assert(epmInfo != null, "epmInfo != null");
            Debug.Assert(epmInfo.PropertyValuePath != null, "The PropertyValuePath should have been initialized by now.");
            Debug.Assert(epmInfo.PropertyValuePath.Length > sourceSegmentIndex, "The PropertyValuePath must be at least as long as the source segment index.");
            Debug.Assert(structuredTypeReference != null, "structuredTypeReference != null");
            Debug.Assert(epmValueCache != null, "epmValueCache != null");

            EpmSourcePathSegment sourceSegment = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string propertyName = sourceSegment.PropertyName;
            bool   lastSegment  = epmInfo.PropertyValuePath.Length == sourceSegmentIndex + 1;

            IEdmStructuredType structuredType        = structuredTypeReference.StructuredDefinition();
            IEdmProperty       edmProperty           = WriterValidationUtils.ValidatePropertyDefined(propertyName, structuredType, this.atomOutputContext.MessageWriterSettings.UndeclaredPropertyBehaviorKinds);
            IEdmTypeReference  propertyTypeReference = null;

            if (edmProperty != null)
            {
                propertyTypeReference = edmProperty.Type;

                // If this is the last part of the path, then it has to be a primitive or atomic collection type otherwise should be a complex type
                if (lastSegment)
                {
                    if (!propertyTypeReference.IsODataPrimitiveTypeKind() && !propertyTypeReference.IsNonEntityCollectionType())
                    {
                        throw new ODataException(ODataErrorStrings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else
                {
                    if (propertyTypeReference.TypeKind() != EdmTypeKind.Complex)
                    {
                        throw new ODataException(ODataErrorStrings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                    }
                }
            }
            else
            {
                Debug.Assert(
                    structuredType.IsOpen,
                    "Only open types can have undeclared properties, otherwise we should have failed in the ValidatePropertyDefined method.");
            }

            ODataProperty property = cachedProperties == null ? null : cachedProperties.FirstOrDefault(p => p.Name == propertyName);

            if (property == null)
            {
                throw new ODataException(ODataErrorStrings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }

            object            propertyValue        = property.Value;
            ODataComplexValue propertyComplexValue = propertyValue as ODataComplexValue;

            if (lastSegment)
            {
                if (propertyValue == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(propertyTypeReference, propertyName, this.WriterBehavior, this.atomOutputContext.Model);
                }
                else
                {
                    // If this property is the last one it has to be either a primitive or collection
                    if (propertyComplexValue != null)
                    {
                        throw new ODataException(ODataErrorStrings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                    else
                    {
                        ODataCollectionValue propertyCollectionValue = propertyValue as ODataCollectionValue;
                        if (propertyCollectionValue != null)
                        {
                            // Validate the type name for the collection
                            TypeNameOracle.ResolveAndValidateTypeNameForValue(
                                this.atomOutputContext.Model,
                                propertyTypeReference,
                                propertyCollectionValue,
                                /*isOpenProperty*/ edmProperty == null);
                        }
                        else
                        {
                            if (propertyValue is ODataStreamReferenceValue)
                            {
                                // Stream properties should not come here, if it were an ODataEntry property it would have been
                                // filtered in ReadEntryPropertyValue() by "epmValueCache.EntryProperties" call.
                                throw new ODataException(ODataErrorStrings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                            }
                            else if (propertyValue is ISpatial)
                            {
                                throw new ODataException(ODataErrorStrings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                            }
                            else if (propertyTypeReference != null)
                            {
                                ValidationUtils.ValidateIsExpectedPrimitiveType(propertyValue, propertyTypeReference);
                            }
                        }
                    }
                }

                return(propertyValue);
            }

            // Otherwise it's in the middle and thus it must be a complex value
            if (propertyComplexValue == null)
            {
                if (propertyValue != null)
                {
                    // It's not a complex value - fail.
                    throw new ODataException(ODataErrorStrings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                else
                {
                    // The value of the property is null, which can be a null complex value
                    // Note that we must not attempt to resolve the type as if the type name was null here, because
                    //  1) We don't need the type for anything anyway (the value is null, this is the end)
                    //  2) If the property is open, trying to resolve a null type name would throw
                    //     but we don't have a null type name, we have a null entire value.
                    return(null);
                }
            }

            IEdmComplexTypeReference complexValueType = TypeNameOracle.ResolveAndValidateTypeNameForValue(
                this.atomOutputContext.Model,
                edmProperty == null ? null : edmProperty.Type,
                propertyComplexValue,
                /*isOpenProperty*/ edmProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(
                       epmInfo,
                       propertyComplexValue,
                       epmValueCache,
                       sourceSegmentIndex + 1,
                       complexValueType));
        }
示例#55
0
        /// <summary>
        /// Creates and returns an ODataCollectionValue from the given value.
        /// </summary>
        /// <param name="collectionItemType">The type of the value.</param>
        /// <param name="propertyName">If the value is a property, then it represents the name of the property. Can be null, for non-property.</param>
        /// <param name="value">The value.</param>
        /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param>
        /// <param name="isDynamicProperty">Whether this collection property is a dynamic property</param>
        /// <param name="setTypeAnnotation">If true, set the type annotation on ODataValue.</param>
        /// <returns>An ODataCollectionValue representing the given value.</returns>
        internal ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, HashSet <object> visitedComplexTypeObjects, bool isDynamicProperty, bool setTypeAnnotation = true)
        {
            Debug.Assert(collectionItemType != null, "collectionItemType != null");

            WebUtil.ValidateCollection(collectionItemType, value, propertyName, isDynamicProperty);

            PrimitiveType ptype;
            bool          isCollectionOfPrimitiveTypes = PrimitiveType.TryGetPrimitiveType(collectionItemType, out ptype);

            ODataCollectionValue collection = new ODataCollectionValue();
            IEnumerable          enumerablePropertyValue = (IEnumerable)value;
            string collectionItemTypeName;
            string collectionTypeName;

            if (isCollectionOfPrimitiveTypes)
            {
                collectionItemTypeName = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType);

                if (enumerablePropertyValue != null)
                {
                    collection.Items = Util.GetEnumerable(
                        enumerablePropertyValue,
                        (val) =>
                    {
                        if (val == null)
                        {
                            return(null);
                        }

                        WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType);
                        return(ConvertPrimitiveValueToRecognizedODataType(val, collectionItemType));
                    });
                }

                // TypeName for primitives should be the EDM name since that's what we will be able to look up in the model
                collectionTypeName = collectionItemTypeName;
            }
            else
            {
                Type collectionItemTypeTmp = Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType;
                bool areEnumItems          = collectionItemTypeTmp.IsEnum();

                // Note that the collectionItemTypeName will be null if the context does not have the ResolveName func.
                collectionItemTypeName = this.requestInfo.ResolveNameFromType(collectionItemType);

                if (enumerablePropertyValue != null)
                {
                    collection.Items = Util.GetEnumerable(
                        enumerablePropertyValue,
                        (val) =>
                    {
                        if (areEnumItems)
                        {
                            if (val == null)
                            {
                                return(new ODataEnumValue(null, collectionItemType.FullName) as ODataValue);
                            }

                            return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(val.ToString(), collectionItemTypeTmp), collectionItemType.FullName) as ODataValue);
                        }
                        else
                        {
                            if (val == null)
                            {
                                return(null);
                            }

                            WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType);
                            return(this.CreateODataComplexValue(val.GetType(), val, propertyName, true /*isCollectionItem*/, visitedComplexTypeObjects)
                                   as ODataValue);
                        }
                    });
                }

                // TypeName for complex types needs to be the client type name (not the one we resolved above) since it will be looked up in the client model
                collectionTypeName = collectionItemType.FullName;
            }

            // Set the type name to use for client type lookups and validation. Because setting this value can cause validation to occur, we will
            // only do it for JSON Light, in order to avoid breaking changes with the WCF Data Services 5.0 release, since it was already shipped without this.
            if (!this.requestInfo.Format.UsingAtom)
            {
                collection.TypeName = GetCollectionName(collectionTypeName);
            }

            // Ideally, we should not set type annotation on collection value.
            // To keep backward compatibility, we'll keep it in request body, but do not include it in url.
            if (setTypeAnnotation)
            {
                string wireTypeName = GetCollectionName(collectionItemTypeName);
                collection.SetAnnotation(new SerializationTypeNameAnnotation {
                    TypeName = wireTypeName
                });
            }

            return(collection);
        }
 public void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isTopLevelProperty, bool isInUri, bool isOpenPropertyType)
 {
     this.WriteCollectionVerifier.Should().NotBeNull("WriteCollectionValue was called.");
     this.WriteCollectionVerifier(collectionValue, metadataTypeReference, isTopLevelProperty, isInUri, isOpenPropertyType);
 }
示例#57
0
        private static void AssertODataCollectionValueAreEqual(ODataCollectionValue collectionValue1, ODataCollectionValue collectionValue2)
        {
            Assert.NotNull(collectionValue1);
            Assert.NotNull(collectionValue2);
            Assert.Equal(collectionValue1.TypeName, collectionValue2.TypeName);
            var itemsArray1 = collectionValue1.Items.OfType <object>().ToArray();
            var itemsArray2 = collectionValue2.Items.OfType <object>().ToArray();

            Assert.Equal(itemsArray1.Length, itemsArray2.Length);
            for (int i = 0; i < itemsArray1.Length; i++)
            {
                var odataValue1 = itemsArray1[i] as ODataValue;
                var odataValue2 = itemsArray2[i] as ODataValue;
                if (odataValue1 != null && odataValue2 != null)
                {
                    AssertODataValueAreEqual(odataValue1, odataValue2);
                }
                else
                {
                    Assert.Equal(itemsArray1[i], itemsArray2[i]);
                }
            }
        }
 /// <summary>
 /// Writes a property with a collection value in ATOM format.
 /// </summary>
 /// <param name="collectionValue">The collection value to write.</param>
 /// <param name="propertyName">The name of the property being written.</param>
 /// <param name="isTopLevel">true if writing a top-level property payload; otherwise false.</param>
 /// <param name="isWritingTopLevelCollection">true if writing a top-level collection payload instead of an entry.</param>
 /// <param name="beforePropertyAction">Action which is called before the property is written, if it's going to be written.</param>
 /// <param name="propertyTypeReference">The type reference of the collection value (or null if no metadata is available).</param>
 /// <param name="isOpenPropertyType">true if this property is undeclared and the owning type is open.</param>
 private void WriteCollectionValueProperty(
     ODataCollectionValue collectionValue,
     string propertyName,
     bool isTopLevel,
     bool isWritingTopLevelCollection,
     Action beforePropertyAction,
     IEdmTypeReference propertyTypeReference,
     bool isOpenPropertyType)
 {
     this.WritePropertyStart(beforePropertyAction, propertyName, collectionValue, isWritingTopLevelCollection, isTopLevel);
     this.WriteCollectionValue(collectionValue, propertyTypeReference, isOpenPropertyType, isWritingTopLevelCollection);
     this.WritePropertyEnd();
 }
示例#59
0
        private object ReadPropertyValue(EntityPropertyMappingInfo epmInfo, IEnumerable <ODataProperty> cachedProperties, int sourceSegmentIndex, IEdmStructuredTypeReference structuredTypeReference, EpmValueCache epmValueCache)
        {
            EpmSourcePathSegment segment            = epmInfo.PropertyValuePath[sourceSegmentIndex];
            string             propertyName         = segment.PropertyName;
            bool               flag                 = epmInfo.PropertyValuePath.Length == (sourceSegmentIndex + 1);
            IEdmStructuredType owningStructuredType = structuredTypeReference.StructuredDefinition();
            IEdmProperty       expectedProperty     = WriterValidationUtils.ValidatePropertyDefined(propertyName, owningStructuredType);

            if (expectedProperty != null)
            {
                if (flag)
                {
                    if (!expectedProperty.Type.IsODataPrimitiveTypeKind() && !expectedProperty.Type.IsNonEntityODataCollectionTypeKind())
                    {
                        throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                    }
                }
                else if (expectedProperty.Type.TypeKind() != EdmTypeKind.Complex)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
            }
            ODataProperty property2 = (cachedProperties == null) ? null : cachedProperties.FirstOrDefault <ODataProperty>(p => (p.Name == propertyName));

            if (property2 == null)
            {
                throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_MissingPropertyOnInstance(propertyName, structuredTypeReference.ODataFullName()));
            }
            object            obj2         = property2.Value;
            ODataComplexValue complexValue = obj2 as ODataComplexValue;

            if (flag)
            {
                if (obj2 == null)
                {
                    WriterValidationUtils.ValidateNullPropertyValue(expectedProperty, this.WriterBehavior, this.atomOutputContext.Model);
                    return(obj2);
                }
                if (complexValue != null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_EndsWithNonPrimitiveType(propertyName));
                }
                ODataCollectionValue value3 = obj2 as ODataCollectionValue;
                if (value3 != null)
                {
                    string str = value3.TypeName;
                    WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref str, EdmTypeKind.Collection, expectedProperty == null);
                    return(obj2);
                }
                if (obj2 is ODataStreamReferenceValue)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.ODataWriter_StreamPropertiesMustBePropertiesOfODataEntry(propertyName));
                }
                if (obj2 is ISpatial)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_OpenPropertySpatialTypeCannotBeMapped(propertyName, epmInfo.DefiningType.FullName()));
                }
                if (expectedProperty != null)
                {
                    ValidationUtils.ValidateIsExpectedPrimitiveType(obj2, expectedProperty.Type);
                }
                return(obj2);
            }
            if (complexValue == null)
            {
                if (obj2 != null)
                {
                    throw new ODataException(Microsoft.Data.OData.Strings.EpmSourceTree_TraversalOfNonComplexType(propertyName));
                }
                return(null);
            }
            string typeName = complexValue.TypeName;
            IEdmComplexTypeReference complexType = WriterValidationUtils.ResolveTypeNameForWriting(this.atomOutputContext.Model, (expectedProperty == null) ? null : expectedProperty.Type, ref typeName, EdmTypeKind.Complex, expectedProperty == null).AsComplexOrNull();

            return(this.ReadComplexPropertyValue(epmInfo, complexValue, epmValueCache, sourceSegmentIndex + 1, complexType));
        }