/// <summary> /// Deserializes the given <paramref name="collectionValue"/> under the given <paramref name="readContext"/>. /// </summary> /// <param name="collectionValue">The <see cref="ODataCollectionValue"/> to deserialize.</param> /// <param name="readContext">The deserializer context.</param> /// <returns>The deserialized collection.</returns> public virtual IEnumerable ReadCollectionValue(ODataCollectionValue collectionValue, ODataDeserializerContext readContext) { if (collectionValue == null) { throw Error.ArgumentNull("collectionValue"); } ODataEdmTypeDeserializer deserializer = DeserializerProvider.GetEdmTypeDeserializer(ElementType); if (deserializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeDeserialized, ElementType.FullName(), typeof(ODataMediaTypeFormatter).Name)); } foreach (object entry in collectionValue.Items) { if (ElementType.IsPrimitive()) { yield return entry; } else { yield return deserializer.ReadInline(entry, readContext); } } }
public void WriteObject_WritesValueReturnedFrom_CreateODataCollectionValue() { // Arrange MemoryStream stream = new MemoryStream(); IODataResponseMessage message = new ODataMessageWrapper(stream); ODataMessageWriter messageWriter = new ODataMessageWriter(message); Mock<ODataCollectionSerializer> serializer = new Mock<ODataCollectionSerializer>(new DefaultODataSerializerProvider()); ODataSerializerContext writeContext = new ODataSerializerContext { RootElementName = "CollectionName", Model = _model }; IEnumerable enumerable = new object[0]; ODataCollectionValue collectionValue = new ODataCollectionValue { TypeName = "NS.Name", Items = new[] { 0, 1, 2 } }; serializer.CallBase = true; serializer .Setup(s => s.CreateODataCollectionValue(enumerable, It.Is<IEdmTypeReference>(e => e.Definition == _edmIntType.Definition), writeContext)) .Returns(collectionValue).Verifiable(); // Act serializer.Object.WriteObject(enumerable, typeof(int[]), messageWriter, writeContext); // Assert serializer.Verify(); stream.Seek(0, SeekOrigin.Begin); XElement element = XElement.Load(stream); Assert.Equal("CollectionName", element.Name.LocalName); Assert.Equal(3, element.Descendants().Count()); Assert.Equal(new[] { "0", "1", "2" }, element.Descendants().Select(e => e.Value)); }
internal void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType) { this.IncreaseRecursionDepth(); base.JsonWriter.StartObjectScope(); string typeName = collectionValue.TypeName; IEdmCollectionTypeReference type = (IEdmCollectionTypeReference) WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string itemTypeNameFromCollection = null; if (typeName != null) { itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation annotation = collectionValue.GetAnnotation<SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (typeName != null) { base.JsonWriter.WriteName("__metadata"); base.JsonWriter.StartObjectScope(); base.JsonWriter.WriteName("type"); base.JsonWriter.WriteValue(typeName); base.JsonWriter.EndObjectScope(); } base.JsonWriter.WriteDataArrayName(); base.JsonWriter.StartArrayScope(); IEnumerable items = collectionValue.Items; if (items != null) { IEdmTypeReference propertyTypeReference = (type == null) ? null : type.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object obj2 in items) { ValidationUtils.ValidateCollectionItem(obj2, false); ODataComplexValue complexValue = obj2 as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue(complexValue, propertyTypeReference, false, duplicatePropertyNamesChecker, collectionValidator); duplicatePropertyNamesChecker.Clear(); } else { this.WritePrimitiveValue(obj2, collectionValidator, propertyTypeReference); } } } base.JsonWriter.EndArrayScope(); base.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }
public void AddTypeNameAnnotationAsNeeded_DoesNotAddAnnotation_InDefaultMetadataMode() { // Arrange ODataCollectionValue value = new ODataCollectionValue(); // Act ODataCollectionSerializer.AddTypeNameAnnotationAsNeeded(value, ODataMetadataLevel.Default); // Assert Assert.Null(value.GetAnnotation<SerializationTypeNameAnnotation>()); }
private object ConvertCollection(ODataCollectionValue collection, ResourceType resourceType) { CollectionResourceType type = resourceType as CollectionResourceType; IList list = Deserializer.CreateNewCollection(); base.RecurseEnter(); foreach (object obj2 in collection.Items) { ResourceType itemType = type.ItemType; list.Add(this.ConvertValue(obj2, ref itemType)); } base.RecurseLeave(); return Deserializer.GetReadOnlyCollection(list); }
private void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, bool isWritingCollection) { this.IncreaseRecursionDepth(); string typeName = collectionValue.TypeName; IEdmCollectionTypeReference type = (IEdmCollectionTypeReference) WriterValidationUtils.ResolveTypeNameForWriting(base.Model, propertyTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string itemTypeNameFromCollection = null; if (typeName != null) { itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation annotation = collectionValue.GetAnnotation<SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (typeName != null) { this.WritePropertyTypeAttribute(typeName); } IEdmTypeReference metadataTypeReference = (type == null) ? null : type.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); IEnumerable items = collectionValue.Items; if (items != null) { DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object obj2 in items) { ValidationUtils.ValidateCollectionItem(obj2, false); base.XmlWriter.WriteStartElement("d", "element", base.MessageWriterSettings.WriterBehavior.ODataNamespace); ODataComplexValue complexValue = obj2 as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue(complexValue, metadataTypeReference, false, isWritingCollection, null, null, duplicatePropertyNamesChecker, collectionValidator, null, null, null); duplicatePropertyNamesChecker.Clear(); } else { this.WritePrimitiveValue(obj2, collectionValidator, metadataTypeReference); } base.XmlWriter.WriteEndElement(); } } this.DecreaseRecursionDepth(); }
private static ODataCollectionValue ReadCollectionParameterValue(ODataCollectionReader collectionReader) { ODataCollectionValue value2; List<object> list = new List<object>(); while (collectionReader.Read()) { switch (collectionReader.State) { case ODataCollectionReaderState.CollectionStart: case ODataCollectionReaderState.CollectionEnd: { continue; } case ODataCollectionReaderState.Value: { list.Add(collectionReader.Item); continue; } case ODataCollectionReaderState.Completed: goto Label_004F; } throw new InvalidOperationException(System.Data.Services.Strings.DataServiceException_GeneralError); } Label_004F: value2 = new ODataCollectionValue(); value2.Items = list; return value2; }
private ODataCollectionValue ReadCollectionValue(IEdmCollectionTypeReference collectionTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { this.IncreaseRecursionDepth(); ODataCollectionValue value2 = new ODataCollectionValue { TypeName = (collectionTypeReference == null) ? payloadTypeName : collectionTypeReference.ODataFullName() }; if (serializationTypeNameAnnotation != null) { value2.SetAnnotation<SerializationTypeNameAnnotation>(serializationTypeNameAnnotation); } base.XmlReader.MoveToElement(); List<object> sourceEnumerable = new List<object>(); if (!base.XmlReader.IsEmptyElement) { base.XmlReader.ReadStartElement(); IEdmTypeReference expectedTypeReference = (collectionTypeReference == null) ? null : collectionTypeReference.ElementType(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); CollectionWithoutExpectedTypeValidator collectionValidator = null; if (collectionTypeReference == null) { string itemTypeNameFromCollection = (payloadTypeName == null) ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); } do { switch (base.XmlReader.NodeType) { case XmlNodeType.Element: if (base.XmlReader.NamespaceEquals(base.XmlReader.ODataNamespace)) { if (!base.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(Microsoft.Data.OData.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(base.XmlReader.LocalName, base.XmlReader.ODataNamespace)); } object item = this.ReadNonEntityValueImplementation(expectedTypeReference, duplicatePropertyNamesChecker, collectionValidator, true, false); base.XmlReader.Read(); ValidationUtils.ValidateCollectionItem(item, false); sourceEnumerable.Add(item); } else { base.XmlReader.Skip(); } break; case XmlNodeType.EndElement: break; default: base.XmlReader.Skip(); break; } } while (base.XmlReader.NodeType != XmlNodeType.EndElement); } value2.Items = new ReadOnlyEnumerable(sourceEnumerable); this.DecreaseRecursionDepth(); return value2; }
public void CreateODataValue_Calls_CreateODataCollectionValue() { // Arrange ODataCollectionValue oDataCollectionValue = new ODataCollectionValue(); var collection = new object[0]; Mock<ODataCollectionSerializer> serializer = new Mock<ODataCollectionSerializer>(new DefaultODataSerializerProvider()); ODataSerializerContext writeContext = new ODataSerializerContext(); serializer.CallBase = true; serializer .Setup(s => s.CreateODataCollectionValue(collection, _edmIntType, writeContext)) .Returns(oDataCollectionValue) .Verifiable(); // Act ODataValue value = serializer.Object.CreateODataValue(collection, _collectionType, writeContext); // Assert serializer.Verify(); Assert.Same(oDataCollectionValue, value); }
/// <summary> /// Reads a collection value. /// </summary> /// <param name="collectionValueTypeReference">The collection type reference of the value.</param> /// <param name="payloadTypeName">The type name read from the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param> /// <returns>The value of the collection.</returns> /// <remarks> /// Pre-Condition: Fails if the current node is not a JsonNodeType.StartObject /// Post-Condition: almost anything - the node after the collection value (after the EndObject) /// </remarks> private ODataCollectionValue ReadCollectionValueImplementation( IEdmCollectionTypeReference collectionValueTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { DebugUtils.CheckNoExternalCallers(); Debug.Assert( collectionValueTypeReference == null || collectionValueTypeReference.IsNonEntityODataCollectionTypeKind(), "If the metadata is specified it must denote a Collection for this method to work."); this.JsonReader.AssertNotBuffering(); ODataVersionChecker.CheckCollectionValue(this.Version); this.IncreaseRecursionDepth(); // Read over the start object this.JsonReader.ReadStartObject(); ODataCollectionValue collectionValue = new ODataCollectionValue(); collectionValue.TypeName = collectionValueTypeReference != null ? collectionValueTypeReference.ODataFullName() : payloadTypeName; if (serializationTypeNameAnnotation != null) { collectionValue.SetAnnotation(serializationTypeNameAnnotation); } List<object> items = null; bool metadataPropertyFound = false; while (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.ReadPropertyName(); if (string.CompareOrdinal(JsonConstants.ODataMetadataName, propertyName) == 0) { // __metadata property if (metadataPropertyFound) { throw new ODataException(o.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper(JsonConstants.ODataMetadataName)); } metadataPropertyFound = true; // Note that we don't need to read the type name again, as we've already read it above in FindTypeNameInPayload. // There's nothing else of interest in the __metadata for collections. this.JsonReader.SkipValue(); } else if (string.CompareOrdinal(JsonConstants.ODataResultsName, propertyName) == 0) { // results property if (items != null) { throw new ODataException(o.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper(JsonConstants.ODataResultsName)); } items = new List<object>(); this.JsonReader.ReadStartArray(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); IEdmTypeReference itemType = null; if (collectionValueTypeReference != null) { itemType = collectionValueTypeReference.CollectionDefinition().ElementType; } // NOTE: we do not support reading JSON without metadata right now so we always have an expected item type; // The collection validator is always null. CollectionWithoutExpectedTypeValidator collectionValidator = null; while (this.JsonReader.NodeType != JsonNodeType.EndArray) { object itemValue = this.ReadNonEntityValueImplementation( itemType, duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true); // Validate the item (for example that it's not null) ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */); // Note that the ReadNonEntityValue already validated that the actual type of the value matches // the expected type (the itemType). items.Add(itemValue); } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "The results value must end with an end array."); this.JsonReader.ReadEndArray(); } else { // Skip over any other property in the collection object this.JsonReader.SkipValue(); } } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndObject, "After all properties of Collection wrapper are read the EndObject node is expected."); this.JsonReader.ReadEndObject(); if (items == null) { // We didn't find any results property. All collections must have the results property. throw new ODataException(o.Strings.ODataJsonPropertyAndValueDeserializer_CollectionWithoutResults); } collectionValue.Items = new ReadOnlyEnumerable(items); this.JsonReader.AssertNotBuffering(); this.DecreaseRecursionDepth(); return collectionValue; }
private static object ConvertCollectionValue(ODataCollectionValue collection, IEdmTypeReference propertyType, ODataDeserializerProvider deserializerProvider, ODataDeserializerContext readContext) { IEdmCollectionTypeReference collectionType = propertyType as IEdmCollectionTypeReference; Contract.Assert(collectionType != null, "The type for collection must be a IEdmCollectionType."); ODataEdmTypeDeserializer deserializer = deserializerProvider.GetEdmTypeDeserializer(collectionType); return deserializer.ReadInline(collection, collectionType, readContext); }
/// <summary> /// Adds the type name annotations required for proper json light serialization. /// </summary> /// <param name="value">The collection value for which the annotations have to be added.</param> /// <param name="metadataLevel">The OData metadata level of the response.</param> protected internal static void AddTypeNameAnnotationAsNeeded(ODataCollectionValue value, ODataMetadataLevel metadataLevel) { // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties // null when values should not be serialized. The TypeName property is different and should always be // provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not // to serialize the type name (a null value prevents serialization). // Note that this annotation should not be used for Atom or JSON verbose formats, as it will interfere with // the correct default behavior for those formats. Contract.Assert(value != null); // Only add an annotation if we want to override ODataLib's default type name serialization behavior. if (ShouldAddTypeNameAnnotation(metadataLevel)) { string typeName; // Provide the type name to serialize (or null to force it not to serialize). if (ShouldSuppressTypeNameSerialization(metadataLevel)) { typeName = null; } else { typeName = value.TypeName; } value.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
/// <inheritdoc/> public override ODataProperty CreateProperty(object graph, string elementName, ODataSerializerContext writeContext) { if (String.IsNullOrWhiteSpace(elementName)) { throw Error.ArgumentNullOrEmpty("elementName"); } if (writeContext == null) { throw Error.ArgumentNull("writeContext"); } ArrayList valueCollection = new ArrayList(); IEdmTypeReference itemType = _edmCollectionType.ElementType(); ODataSerializer itemSerializer = SerializerProvider.GetEdmTypeSerializer(itemType); if (itemSerializer == null) { throw Error.NotSupported(SRResources.TypeCannotBeSerialized, itemType.FullName(), typeof(ODataMediaTypeFormatter).Name); } IEnumerable enumerable = graph as IEnumerable; if (enumerable != null) { foreach (object item in enumerable) { valueCollection.Add(itemSerializer.CreateProperty(item, "element", writeContext).Value); } } string typeName = _edmCollectionType.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 new ODataProperty() { Name = elementName, Value = value }; }
public void AddTypeNameAnnotationAsNeeded_AddsAnnotation_InJsonLightMetadataMode() { // Arrange string expectedTypeName = "TypeName"; ODataCollectionValue value = new ODataCollectionValue { TypeName = expectedTypeName }; // Act ODataCollectionSerializer.AddTypeNameAnnotationAsNeeded(value, ODataMetadataLevel.FullMetadata); // Assert SerializationTypeNameAnnotation annotation = value.GetAnnotation<SerializationTypeNameAnnotation>(); Assert.NotNull(annotation); // Guard Assert.Equal(expectedTypeName, annotation.TypeName); }
private void SetEpmValueForSegment(EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, List<ODataProperty> existingProperties, object propertyValue) { string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName; if (!epmInfo.Attribute.KeepInContent) { IEdmTypeReference type; ODataProperty property = existingProperties.FirstOrDefault<ODataProperty>(p => string.CompareOrdinal(p.Name, propertyName) == 0); ODataComplexValue value2 = null; if (property != null) { value2 = property.Value as ODataComplexValue; if (value2 == null) { return; } } IEdmProperty property2 = segmentStructuralTypeReference.FindProperty(propertyName); if ((property2 == null) && (propertyValuePathIndex != (epmInfo.PropertyValuePath.Length - 1))) { throw new ODataException(Microsoft.Data.OData.Strings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath)); } if ((property2 == null) || (this.MessageReaderSettings.DisablePrimitiveTypeConversion && property2.Type.IsODataPrimitiveTypeKind())) { type = EdmCoreModel.Instance.GetString(true); } else { type = property2.Type; } switch (type.TypeKind()) { case EdmTypeKind.Primitive: object obj2; if (type.IsStream()) { throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty)); } if (propertyValue == null) { ReaderValidationUtils.ValidateNullValue(this.atomInputContext.Model, type, this.atomInputContext.MessageReaderSettings, true, this.atomInputContext.Version); obj2 = null; } else { obj2 = AtomValueUtils.ConvertStringToPrimitive((string) propertyValue, type.AsPrimitive()); } this.AddEpmPropertyValue(existingProperties, propertyName, obj2, segmentStructuralTypeReference.IsODataEntityTypeKind()); return; case EdmTypeKind.Complex: { if (value2 == null) { value2 = new ODataComplexValue { TypeName = type.ODataFullName(), Properties = new ReadOnlyEnumerable<ODataProperty>() }; this.AddEpmPropertyValue(existingProperties, propertyName, value2, segmentStructuralTypeReference.IsODataEntityTypeKind()); } IEdmComplexTypeReference reference2 = type.AsComplex(); this.SetEpmValueForSegment(epmInfo, propertyValuePathIndex + 1, reference2, ReaderUtils.GetPropertiesList(value2.Properties), propertyValue); return; } case EdmTypeKind.Collection: { ODataCollectionValue value4 = new ODataCollectionValue { TypeName = type.ODataFullName(), Items = new ReadOnlyEnumerable((List<object>) propertyValue) }; this.AddEpmPropertyValue(existingProperties, propertyName, value4, segmentStructuralTypeReference.IsODataEntityTypeKind()); return; } } throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind)); } }
/// <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(); // "__metadata": { "type": "typename" } // If the CollectionValue has type information write out the metadata and the type in it. string typeName = collectionValue.TypeName; // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(this.Model, metadataTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string collectionItemTypeName = null; if (typeName != null) { collectionItemTypeName = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation serializationTypeNameAnnotation = collectionValue.GetAnnotation<SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { typeName = serializationTypeNameAnnotation.TypeName; } if (typeName != null) { // Write the __metadata object this.JsonWriter.WriteName(JsonConstants.ODataMetadataName); this.JsonWriter.StartObjectScope(); // "type": "typename" this.JsonWriter.WriteName(JsonConstants.ODataMetadataTypeName); this.JsonWriter.WriteValue(typeName); // End the __metadata this.JsonWriter.EndObjectScope(); } // "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(); }
private static object ConvertCollectionValue(ODataCollectionValue collection, IEdmTypeReference propertyType, ODataDeserializerProvider deserializerProvider, ODataDeserializerReadContext readContext) { IEdmCollectionType collectionType = propertyType as IEdmCollectionType; Contract.Assert(collectionType != null, "The type for collection must be a IEdmCollectionType."); IList collectionList = CreateNewCollection(); RecurseEnter(readContext); Contract.Assert(collection.Items != null, "The ODataLib reader should always populate the ODataCollectionValue.Items collection."); foreach (object odataItem in collection.Items) { IEdmTypeReference itemType = collectionType.ElementType; collectionList.Add(ConvertValue(odataItem, ref itemType, deserializerProvider, readContext)); } RecurseLeave(readContext); return collectionList; }
/// <summary> /// Read a collection from the reader. /// </summary> /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param> /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param> /// <returns>The value read from the payload.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - the element to read the value for. /// XmlNodeType.Attribute - an attribute on the element to read the value for. /// Post-Condition: XmlNodeType.Element - the element was empty. /// XmlNodeType.EndElement - the element had some value. /// /// Note that this method will not read null values, those should be handled by the caller already. /// </remarks> private ODataCollectionValue ReadCollectionValue( IEdmCollectionTypeReference collectionTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute); Debug.Assert( collectionTypeReference == null || collectionTypeReference.IsNonEntityODataCollectionTypeKind(), "If the metadata is specified it must denote a collection for this method to work."); this.IncreaseRecursionDepth(); ODataCollectionValue collectionValue = new ODataCollectionValue(); // If we have a metadata type for the collection, use that type name // otherwise use the type name from the payload (if there was any). collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName(); if (serializationTypeNameAnnotation != null) { collectionValue.SetAnnotation(serializationTypeNameAnnotation); } // Move to the element (so that if we were on an attribute we can test the element for being empty) this.XmlReader.MoveToElement(); List<object> items = new List<object>(); // Empty collections are valid - they have no items if (!this.XmlReader.IsEmptyElement) { // Read over the collection element to its first child node (or end-element) this.XmlReader.ReadStartElement(); IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); CollectionWithoutExpectedTypeValidator collectionValidator = null; if (collectionTypeReference == null) { // Parse the type name from the payload (if any), extract the item type name and construct a collection validator string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName); } do { switch (this.XmlReader.NodeType) { case XmlNodeType.Element: if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(o.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataNamespace)); } // We don't support EPM for collections so it is fine to say that EPM is not present object itemValue = this.ReadNonEntityValueImplementation( itemTypeReference, duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /*epmPresent*/ false); // read over the end tag of the element or the start tag if the element was empty. this.XmlReader.Read(); // Validate the item (for example that it's not null) ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */); // Note that the ReadNonEntityValue already validated that the actual type of the value matches // the expected type (the itemType). items.Add(itemValue); } else { this.XmlReader.Skip(); } break; case XmlNodeType.EndElement: // End of the collection. break; default: // Non-element so for example a text node, just ignore this.XmlReader.Skip(); break; } } while (this.XmlReader.NodeType != XmlNodeType.EndElement); } collectionValue.Items = new ReadOnlyEnumerable(items); this.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values."); this.DecreaseRecursionDepth(); return collectionValue; }
internal static void AddTypeNameAnnotationAsNeeded(ODataCollectionValue value, ODataMetadataLevel metadataLevel) { Contract.Assert(value != null); if (ShouldAddTypeNameAnnotation(metadataLevel)) { string typeName; if (ShouldSuppressTypeNameSerialization(metadataLevel)) { typeName = null; } else { typeName = value.TypeName; } value.SetAnnotation<SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
private void SetEpmValueForSegment( EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, List<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."); if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1) { throw new ODataException(o.Strings.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(o.Strings.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); 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, ReaderUtils.GetPropertiesList(existingComplexValue.Properties), propertyValue); break; case EdmTypeKind.Collection: Debug.Assert(propertyType.IsNonEntityODataCollectionTypeKind(), "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(o.Strings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind)); } }
/// <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 ODataCollectionValue CreateODataCollectionValue(IEnumerable enumerable, IEdmTypeReference elementType, ODataSerializerContext writeContext) { if (writeContext == null) { throw Error.ArgumentNull("writeContext"); } if (elementType == null) { throw Error.ArgumentNull("elementType"); } ArrayList valueCollection = new ArrayList(); if (enumerable != null) { ODataEdmTypeSerializer itemSerializer = null; foreach (object item in enumerable) { itemSerializer = itemSerializer ?? SerializerProvider.GetEdmTypeSerializer(elementType); if (itemSerializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, elementType.FullName(), typeof(ODataMediaTypeFormatter).Name)); } // ODataCollectionWriter expects the individual elements in the collection to be the underlying values and not ODataValues. valueCollection.Add(itemSerializer.CreateODataValue(item, elementType, 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; }
private ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, List<object> visitedComplexTypeObjects) { PrimitiveType type; string edmType; Func<object, object> valueConverter = null; Func<object, ODataComplexValue> func2 = null; WebUtil.ValidateCollection(collectionItemType, value, propertyName); bool flag = PrimitiveType.TryGetPrimitiveType(collectionItemType, out type); ODataCollectionValue value2 = new ODataCollectionValue(); IEnumerable enumerable = (IEnumerable) value; if (flag) { edmType = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType); if (valueConverter == null) { valueConverter = delegate (object val) { WebUtil.ValidateCollectionItem(val); WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType); return GetPrimitiveValue(val, collectionItemType); }; } value2.Items = Util.GetEnumerable<object>(enumerable, valueConverter); } else { edmType = this.requestInfo.ResolveNameFromType(collectionItemType); if (func2 == null) { func2 = delegate (object val) { WebUtil.ValidateCollectionItem(val); WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType); return this.CreateODataComplexValue(collectionItemType, val, propertyName, true, visitedComplexTypeObjects); }; } value2.Items = Util.GetEnumerable<ODataComplexValue>(enumerable, func2); } string str2 = (edmType == null) ? null : string.Format(CultureInfo.InvariantCulture, "Collection({0})", new object[] { edmType }); SerializationTypeNameAnnotation annotation = new SerializationTypeNameAnnotation { TypeName = str2 }; value2.SetAnnotation<SerializationTypeNameAnnotation>(annotation); return value2; }
internal static string ConvertToUriCollectionLiteral(ODataCollectionValue collectionValue, IEdmModel model, ODataVersion version) { ExceptionUtils.CheckArgumentNotNull<ODataCollectionValue>(collectionValue, "collectionValue"); ExceptionUtils.CheckArgumentNotNull<IEdmModel>(model, "model"); ODataVersionChecker.CheckCollectionValue(version); StringBuilder sb = new StringBuilder(); using (TextWriter writer = new StringWriter(sb, CultureInfo.InvariantCulture)) { JsonWriter jsonWriter = new JsonWriter(writer, false); ODataMessageWriterSettings messageWriterSettings = new ODataMessageWriterSettings { Version = new ODataVersion?(version) }; using (ODataJsonOutputContext context = ODataJsonOutputContext.Create(ODataFormat.VerboseJson, jsonWriter, messageWriterSettings, false, model, null)) { new ODataJsonPropertyAndValueSerializer(context).WriteCollectionValue(collectionValue, null, false); } } return sb.ToString(); }
private static object ConvertCollectionValue(ODataCollectionValue collection, IEdmTypeReference propertyType, ODataDeserializerProvider deserializerProvider, ODataDeserializerContext readContext) { IEdmCollectionTypeReference collectionType = propertyType as IEdmCollectionTypeReference; Contract.Assert(collectionType != null, "The type for collection must be a IEdmCollectionType."); IList collectionList = CreateNewCollection(EdmLibHelpers.GetClrType(collectionType.ElementType(), deserializerProvider.EdmModel)); RecurseEnter(readContext); Contract.Assert(collection.Items != null, "The ODataLib reader should always populate the ODataCollectionValue.Items collection."); foreach (object odataItem in collection.Items) { IEdmTypeReference itemType = collectionType.ElementType(); EdmTypeKind propertyKind; collectionList.Add(ConvertValue(odataItem, ref itemType, deserializerProvider, readContext, out propertyKind)); Contract.Assert(propertyKind != EdmTypeKind.Primitive, "no collection property support yet."); } RecurseLeave(readContext); return collectionList; }
private ODataCollectionValue ReadCollectionValueImplementation(IEdmCollectionTypeReference collectionValueTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { ODataVersionChecker.CheckCollectionValue(base.Version); this.IncreaseRecursionDepth(); base.JsonReader.ReadStartObject(); ODataCollectionValue value2 = new ODataCollectionValue { TypeName = (collectionValueTypeReference != null) ? collectionValueTypeReference.ODataFullName() : payloadTypeName }; if (serializationTypeNameAnnotation != null) { value2.SetAnnotation<SerializationTypeNameAnnotation>(serializationTypeNameAnnotation); } List<object> sourceEnumerable = null; bool flag = false; while (base.JsonReader.NodeType == JsonNodeType.Property) { string strB = base.JsonReader.ReadPropertyName(); if (string.CompareOrdinal("__metadata", strB) == 0) { if (flag) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper("__metadata")); } flag = true; base.JsonReader.SkipValue(); } else { if (string.CompareOrdinal("results", strB) == 0) { if (sourceEnumerable != null) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper("results")); } sourceEnumerable = new List<object>(); base.JsonReader.ReadStartArray(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); IEdmTypeReference expectedTypeReference = null; if (collectionValueTypeReference != null) { expectedTypeReference = collectionValueTypeReference.CollectionDefinition().ElementType; } CollectionWithoutExpectedTypeValidator collectionValidator = null; while (base.JsonReader.NodeType != JsonNodeType.EndArray) { object item = this.ReadNonEntityValueImplementation(expectedTypeReference, duplicatePropertyNamesChecker, collectionValidator, true); ValidationUtils.ValidateCollectionItem(item, false); sourceEnumerable.Add(item); } base.JsonReader.ReadEndArray(); continue; } base.JsonReader.SkipValue(); } } base.JsonReader.ReadEndObject(); if (sourceEnumerable == null) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_CollectionWithoutResults); } value2.Items = new ReadOnlyEnumerable(sourceEnumerable); this.DecreaseRecursionDepth(); return value2; }
/// <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 (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 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(); string typeName = collectionValue.TypeName; // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(this.Model, propertyTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string collectionItemTypeName = null; if (typeName != null) { collectionItemTypeName = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation serializationTypeNameAnnotation = collectionValue.GetAnnotation<SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { typeName = serializationTypeNameAnnotation.TypeName; } if (typeName != null) { this.WritePropertyTypeAttribute(typeName); } 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, false /* isStreamable */); this.XmlWriter.WriteStartElement(AtomConstants.ODataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, this.MessageWriterSettings.WriterBehavior.ODataNamespace); 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 /* epmValueCache */, null /* epmSourcePathSegment */, null /* projectedProperties */); duplicatePropertyNamesChecker.Clear(); } else { Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)"); Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)"); this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference); } this.XmlWriter.WriteEndElement(); } } this.DecreaseRecursionDepth(); }