/// <summary> /// Creates the <see cref="ODataProperty"/> to be written for the given entity and the structural property. /// </summary> /// <param name="structuralProperty">The EDM structural property being written.</param> /// <param name="entityInstanceContext">The context for the entity instance being written.</param> /// <returns>The <see cref="ODataProperty"/> to write.</returns> public virtual ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, EntityInstanceContext entityInstanceContext) { if (structuralProperty == null) { throw Error.ArgumentNull("structuralProperty"); } if (entityInstanceContext == null) { throw Error.ArgumentNull("entityInstanceContext"); } ODataSerializerContext writeContext = entityInstanceContext.SerializerContext; ODataEdmTypeSerializer serializer = SerializerProvider.GetEdmTypeSerializer(structuralProperty.Type); if (serializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, structuralProperty.Type.FullName(), typeof(ODataMediaTypeFormatter).Name)); } object propertyValue = entityInstanceContext.GetPropertyValue(structuralProperty.Name); return(serializer.CreateProperty(propertyValue, structuralProperty.Type, structuralProperty.Name, writeContext)); }
private void WriteExpandedNavigationProperty( KeyValuePair <IEdmNavigationProperty, SelectExpandClause> navigationPropertyToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer) { Contract.Assert(entityInstanceContext != null); Contract.Assert(writer != null); IEdmNavigationProperty navigationProperty = navigationPropertyToExpand.Key; SelectExpandClause selectExpandClause = navigationPropertyToExpand.Value; object propertyValue = entityInstanceContext.GetPropertyValue(navigationProperty.Name); if (propertyValue != null) { // create the serializer context for the expanded item. ODataSerializerContext nestedWriteContext = new ODataSerializerContext(entityInstanceContext, selectExpandClause, navigationProperty); // write object. ODataEdmTypeSerializer serializer = SerializerProvider.GetEdmTypeSerializer(navigationProperty.Type); if (serializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, navigationProperty.Type.ToTraceString(), typeof(ODataMediaTypeFormatter).Name)); } serializer.WriteObjectInline(propertyValue, navigationProperty.Type, writer, nestedWriteContext); } }
/// <summary> /// Creates an <see cref="ODataComplexValue"/> for the object represented by <paramref name="graph"/>. /// </summary> /// <param name="graph">The value of the <see cref="ODataComplexValue"/> to be created.</param> /// <param name="complexType">The EDM complex type of the object.</param> /// <param name="writeContext">The serializer context.</param> /// <returns>The created <see cref="ODataComplexValue"/>.</returns> public virtual ODataComplexValue CreateODataComplexValue(object graph, IEdmComplexTypeReference complexType, ODataSerializerContext writeContext) { if (writeContext == null) { throw Error.ArgumentNull("writeContext"); } if (graph == null || graph is NullEdmComplexObject) { return(null); } IEdmComplexObject complexObject = graph as IEdmComplexObject ?? new TypedEdmComplexObject(graph, complexType, writeContext.Model); List <ODataProperty> propertyCollection = new List <ODataProperty>(); foreach (IEdmProperty property in complexType.ComplexDefinition().Properties()) { IEdmTypeReference propertyType = property.Type; ODataEdmTypeSerializer propertySerializer = SerializerProvider.GetEdmTypeSerializer(propertyType); if (propertySerializer == null) { throw Error.NotSupported(SRResources.TypeCannotBeSerialized, propertyType.FullName(), typeof(ODataMediaTypeFormatter).Name); } object propertyValue; if (complexObject.TryGetPropertyValue(property.Name, out propertyValue)) { propertyCollection.Add( propertySerializer.CreateProperty(propertyValue, property.Type, property.Name, writeContext)); } } // Try to add the dynamic properties if the complex type is open. if (complexType.ComplexDefinition().IsOpen) { List <ODataProperty> dynamicProperties = AppendDynamicProperties(complexObject, complexType, writeContext, propertyCollection, new string[0]); if (dynamicProperties != null) { propertyCollection.AddRange(dynamicProperties); } } string typeName = complexType.FullName(); ODataComplexValue value = new ODataComplexValue() { Properties = propertyCollection, TypeName = typeName }; AddTypeNameAnnotationAsNeeded(value, writeContext.MetadataLevel); return(value); }
private void WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext) { Contract.Assert(writer != null); Contract.Assert(writeContext != null); Contract.Assert(enumerable != null); Contract.Assert(feedType != null); IEdmEntityTypeReference elementType = GetEntityType(feedType); ODataFeed feed = CreateODataFeed(enumerable, feedType.AsCollection(), writeContext); if (feed == null) { throw new SerializationException(Error.Format(SRResources.CannotSerializerNull, Feed)); } ODataEdmTypeSerializer entrySerializer = SerializerProvider.GetEdmTypeSerializer(elementType); if (entrySerializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, elementType.FullName(), typeof(ODataMediaTypeFormatter).Name)); } // save this for later to support JSON odata.streaming. Uri nextPageLink = feed.NextPageLink; feed.NextPageLink = null; writer.WriteStart(feed); foreach (object entry in enumerable) { if (entry == null) { throw new SerializationException(SRResources.NullElementInCollection); } entrySerializer.WriteObjectInline(entry, elementType, writer, writeContext); } // Subtle and suprising behavior: If the NextPageLink property is set before calling WriteStart(feed), // the next page link will be written early in a manner not compatible with odata.streaming=true. Instead, if // the next page link is not set when calling WriteStart(feed) but is instead set later on that feed // object before calling WriteEnd(), the next page link will be written at the end, as required for // odata.streaming=true support. if (nextPageLink != null) { feed.NextPageLink = nextPageLink; } writer.WriteEnd(); }
/// <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) { 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(ODataMediaTypeFormatter).Name)); } // ODataCollectionWriter expects the individual elements in the collection to be the underlying // values and not ODataValues. valueCollection.Add( itemSerializer.CreateODataValue(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); }
private void WriteExpandedNavigationProperty( KeyValuePair <IEdmNavigationProperty, SelectExpandClause> navigationPropertyToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer) { Contract.Assert(entityInstanceContext != null); Contract.Assert(writer != null); IEdmNavigationProperty navigationProperty = navigationPropertyToExpand.Key; SelectExpandClause selectExpandClause = navigationPropertyToExpand.Value; object propertyValue = entityInstanceContext.GetPropertyValue(navigationProperty.Name); if (propertyValue == null) { if (navigationProperty.Type.IsCollection()) { // A navigation property whose Type attribute specifies a collection, the collection always exists, // it may just be empty. // If a collection of entities can be related, it is represented as a JSON array. An empty // collection of entities (one that contains no entities) is represented as an empty JSON array. writer.WriteStart(new ODataFeed()); } else { // If at most one entity can be related, the value is null if no entity is currently related. writer.WriteStart(entry: null); } writer.WriteEnd(); } else { // create the serializer context for the expanded item. ODataSerializerContext nestedWriteContext = new ODataSerializerContext(entityInstanceContext, selectExpandClause, navigationProperty); // write object. ODataEdmTypeSerializer serializer = SerializerProvider.GetEdmTypeSerializer(navigationProperty.Type); if (serializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, navigationProperty.Type.ToTraceString(), typeof(ODataMediaTypeFormatter).Name)); } serializer.WriteObjectInline(propertyValue, navigationProperty.Type, writer, nestedWriteContext); } }
internal void AppendDynamicProperties(object graph, IEdmComplexTypeReference complexType, ODataSerializerContext writeContext, List <ODataProperty> propertyCollection) { PropertyInfo dynamicPropertyInfo = EdmLibHelpers.GetDynamicPropertyDictionary(complexType.ComplexDefinition(), writeContext.Model); if (dynamicPropertyInfo != null) { IDictionary <string, object> dynamicPropertyDictionary = dynamicPropertyInfo.GetValue(graph) as IDictionary <string, object>; if (dynamicPropertyDictionary != null) { // build a HashSet to store the declared property names. // It is used to make sure the dynamic property name is different with the declared property name. HashSet <string> declaredPropertyNameSet = new HashSet <string>(propertyCollection.Select(a => a.Name)); foreach (KeyValuePair <string, object> dynamicProperty in dynamicPropertyDictionary) { if (dynamicProperty.Value == null) { continue; // skip the null object } Type valueType = dynamicProperty.Value.GetType(); IEdmTypeReference edmTypeReference = writeContext.Model.GetEdmTypeReference(valueType); ODataEdmTypeSerializer propertySerializer = SerializerProvider.GetEdmTypeSerializer(edmTypeReference); if (propertySerializer == null) { throw Error.NotSupported(SRResources.TypeCannotBeSerialized, valueType.FullName, typeof(ODataComplexTypeSerializer).Name); } // try to make sure the dynamic property name is not used as declared property name. if (declaredPropertyNameSet.Contains(dynamicProperty.Key)) { throw Error.InvalidOperation(SRResources.DynamicPropertyNameAlreadyUsedAsDeclaredPropertyName, dynamicProperty.Key, complexType.FullName()); } propertyCollection.Add(propertySerializer.CreateProperty( dynamicProperty.Value, edmTypeReference, dynamicProperty.Key, writeContext)); } } } }
/// <summary> /// Creates the <see cref="ODataProperty"/> to be written for the given entity and the structural property. /// </summary> /// <param name="structuralProperty">The EDM structural property being written.</param> /// <param name="entityInstanceContext">The context for the entity instance being written.</param> /// <returns>The <see cref="ODataProperty"/> to write.</returns> public virtual ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, EntityInstanceContext entityInstanceContext) { if (structuralProperty == null) { throw Error.ArgumentNull("structuralProperty"); } if (entityInstanceContext == null) { throw Error.ArgumentNull("entityInstanceContext"); } ODataSerializerContext writeContext = entityInstanceContext.SerializerContext; ODataEdmTypeSerializer serializer = SerializerProvider.GetEdmTypeSerializer(structuralProperty.Type); if (serializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, structuralProperty.Type.FullName(), typeof(ODataMediaTypeFormatter).Name)); } object propertyValue = entityInstanceContext.GetPropertyValue(structuralProperty.Name); IEdmTypeReference propertyType = structuralProperty.Type; if (propertyValue != null) { if (!propertyType.IsPrimitive() && !propertyType.IsEnum()) { IEdmTypeReference actualType = writeContext.GetEdmType(propertyValue, propertyValue.GetType()); if (propertyType != null && propertyType != actualType) { propertyType = actualType; } } } return(serializer.CreateProperty(propertyValue, propertyType, structuralProperty.Name, writeContext)); }
internal List <ODataProperty> AppendDynamicProperties(object source, IEdmStructuredTypeReference structuredType, ODataSerializerContext writeContext, List <ODataProperty> declaredProperties, string[] selectedDynamicProperties) { Contract.Assert(source != null); Contract.Assert(structuredType != null); Contract.Assert(writeContext != null); Contract.Assert(writeContext.Model != null); PropertyInfo dynamicPropertyInfo = EdmLibHelpers.GetDynamicPropertyDictionary( structuredType.StructuredDefinition(), writeContext.Model); IEdmStructuredObject structuredObject = source as IEdmStructuredObject; object value; IDelta delta = source as IDelta; if (delta == null) { if (dynamicPropertyInfo == null || structuredObject == null || !structuredObject.TryGetPropertyValue(dynamicPropertyInfo.Name, out value) || value == null) { return(null); } } else { value = ((EdmStructuredObject)structuredObject).TryGetDynamicProperties(); } IDictionary <string, object> dynamicPropertyDictionary = (IDictionary <string, object>)value; // Build a HashSet to store the declared property names. // It is used to make sure the dynamic property name is different from all declared property names. HashSet <string> declaredPropertyNameSet = new HashSet <string>(declaredProperties.Select(p => p.Name)); List <ODataProperty> dynamicProperties = new List <ODataProperty>(); IEnumerable <KeyValuePair <string, object> > dynamicPropertiesToSelect = dynamicPropertyDictionary.Where( x => !selectedDynamicProperties.Any() || selectedDynamicProperties.Contains(x.Key)); foreach (KeyValuePair <string, object> dynamicProperty in dynamicPropertiesToSelect) { if (String.IsNullOrEmpty(dynamicProperty.Key) || dynamicProperty.Value == null) { continue; // skip the null object } if (declaredPropertyNameSet.Contains(dynamicProperty.Key)) { throw Error.InvalidOperation(SRResources.DynamicPropertyNameAlreadyUsedAsDeclaredPropertyName, dynamicProperty.Key, structuredType.FullName()); } IEdmTypeReference edmTypeReference = writeContext.GetEdmType(dynamicProperty.Value, dynamicProperty.Value.GetType()); if (edmTypeReference == null) { throw Error.NotSupported(SRResources.TypeOfDynamicPropertyNotSupported, dynamicProperty.Value.GetType().FullName, dynamicProperty.Key); } ODataEdmTypeSerializer propertySerializer = SerializerProvider.GetEdmTypeSerializer(edmTypeReference); if (propertySerializer == null) { throw Error.NotSupported(SRResources.DynamicPropertyCannotBeSerialized, dynamicProperty.Key, edmTypeReference.FullName()); } dynamicProperties.Add(propertySerializer.CreateProperty( dynamicProperty.Value, edmTypeReference, dynamicProperty.Key, writeContext)); } return(dynamicProperties); }
public static ODataSerializerProvider GetMockODataSerializerProvider(ODataEdmTypeSerializer serializer) { Mock<ODataSerializerProvider> serializerProvider = new Mock<ODataSerializerProvider>(); serializerProvider.Setup(sp => sp.GetEdmTypeSerializer(It.IsAny<IEdmTypeReference>())).Returns(serializer); return serializerProvider.Object; }
/// <summary> /// Creates an <see cref="ODataComplexValue"/> for the object represented by <paramref name="graph"/>. /// </summary> /// <param name="graph">The value of the <see cref="ODataComplexValue"/> to be created.</param> /// <param name="complexType">The EDM complex type of the object.</param> /// <param name="writeContext">The serializer context.</param> /// <returns>The created <see cref="ODataComplexValue"/>.</returns> public virtual ODataComplexValue CreateODataComplexValue(object graph, IEdmComplexTypeReference complexType, ODataSerializerContext writeContext) { if (writeContext == null) { throw Error.ArgumentNull("writeContext"); } if (graph == null || graph is NullEdmComplexObject) { return(null); } IEdmComplexObject complexObject = graph as IEdmComplexObject ?? new TypedEdmComplexObject(graph, complexType, writeContext.Model); List <IEdmProperty> settableProperties = new List <IEdmProperty>(complexType.ComplexDefinition().Properties()); if (complexObject.IsDeltaResource()) { IDelta deltaProperty = graph as IDelta; Contract.Assert(deltaProperty != null); IEnumerable <string> changedProperties = deltaProperty.GetChangedPropertyNames(); settableProperties = settableProperties.Where(p => changedProperties.Contains(p.Name)).ToList(); } List <ODataProperty> propertyCollection = new List <ODataProperty>(); foreach (IEdmProperty property in settableProperties) { IEdmTypeReference propertyType = property.Type; ODataEdmTypeSerializer propertySerializer = SerializerProvider.GetEdmTypeSerializer(propertyType); if (propertySerializer == null) { throw Error.NotSupported(SRResources.TypeCannotBeSerialized, propertyType.FullName(), typeof(ODataMediaTypeFormatter).Name); } object propertyValue; if (complexObject.TryGetPropertyValue(property.Name, out propertyValue)) { if (propertyValue != null && propertyType != null && propertyType.IsComplex()) { IEdmTypeReference actualType = writeContext.GetEdmType(propertyValue, propertyValue.GetType()); if (actualType != null && propertyType != actualType) { propertyType = actualType; } } var odataProperty = propertySerializer.CreateProperty(propertyValue, propertyType, property.Name, writeContext); if (odataProperty != null) { propertyCollection.Add(odataProperty); } } } // Try to add the dynamic properties if the complex type is open. if (complexType.ComplexDefinition().IsOpen) { List <ODataProperty> dynamicProperties = AppendDynamicProperties(complexObject, complexType, writeContext, propertyCollection, new string[0]); if (dynamicProperties != null) { propertyCollection.AddRange(dynamicProperties); } } string typeName = complexType.FullName(); ODataComplexValue value = new ODataComplexValue() { Properties = propertyCollection, TypeName = typeName }; AddTypeNameAnnotationAsNeeded(value, writeContext.MetadataLevel); return(value); }
private void WriteResourceSet(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext) { Contract.Assert(writer != null); Contract.Assert(writeContext != null); Contract.Assert(enumerable != null); Contract.Assert(resourceSetType != null); IEdmStructuredTypeReference elementType = GetResourceType(resourceSetType); ODataResourceSet resourceSet = CreateResourceSet(enumerable, resourceSetType.AsCollection(), writeContext); if (resourceSet == null) { throw new SerializationException(Error.Format(SRResources.CannotSerializerNull, ResourceSet)); } IEdmEntitySetBase entitySet = writeContext.NavigationSource as IEdmEntitySetBase; if (entitySet == null) { resourceSet.SetSerializationInfo(new ODataResourceSerializationInfo { IsFromCollection = true, NavigationSourceEntityTypeName = elementType.FullName(), NavigationSourceKind = EdmNavigationSourceKind.UnknownEntitySet, NavigationSourceName = null }); } ODataEdmTypeSerializer resourceSerializer = SerializerProvider.GetEdmTypeSerializer(elementType); if (resourceSerializer == null) { throw new SerializationException( Error.Format(SRResources.TypeCannotBeSerialized, elementType.FullName(), typeof(ODataMediaTypeFormatter).Name)); } // save this for later to support JSON odata.streaming. Uri nextPageLink = resourceSet.NextPageLink; resourceSet.NextPageLink = null; writer.WriteStart(resourceSet); foreach (object item in enumerable) { if (item == null || item is NullEdmComplexObject) { if (elementType.IsEntity()) { throw new SerializationException(SRResources.NullElementInCollection); } // for null complex element, it can be serialized as "null" in the collection. writer.WriteStart(resource: null); writer.WriteEnd(); } else { resourceSerializer.WriteObjectInline(item, elementType, writer, writeContext); } } // Subtle and surprising behavior: If the NextPageLink property is set before calling WriteStart(resourceSet), // the next page link will be written early in a manner not compatible with odata.streaming=true. Instead, if // the next page link is not set when calling WriteStart(resourceSet) but is instead set later on that resourceSet // object before calling WriteEnd(), the next page link will be written at the end, as required for // odata.streaming=true support. if (nextPageLink != null) { resourceSet.NextPageLink = nextPageLink; } writer.WriteEnd(); }