/// <summary> /// Converts the given JSON value to the expected type as per OData conversion rules for JSON values. /// </summary> /// <param name="value">Value to the converted.</param> /// <param name="primitiveTypeReference">Type reference to which the value needs to be converted.</param> /// <param name="messageReaderSettings">The message reader settings used for reading.</param> /// <param name="version">The version of the OData protocol used for reading.</param> /// <param name="validateNullValue">true to validate null values; otherwise false.</param> /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param> /// <returns>Object which is in sync with the property type (modulo the V1 exception of converting numbers to non-compatible target types).</returns> internal static object ConvertValue( object value, IEdmPrimitiveTypeReference primitiveTypeReference, ODataMessageReaderSettings messageReaderSettings, ODataVersion version, bool validateNullValue, string propertyName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(primitiveTypeReference != null, "primitiveTypeReference != null"); //// NOTE: this method was copied from WCF DS (and changed to take a type reference instead of a CLR target type) if (value == null) { // Only primitive type references are validated. Core model is sufficient. ReaderValidationUtils.ValidateNullValue(EdmCoreModel.Instance, primitiveTypeReference, messageReaderSettings, validateNullValue, version, propertyName); return(null); } try { Type targetType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false); ODataReaderBehavior readerBehavior = messageReaderSettings.ReaderBehavior; string stringValue = value as string; if (stringValue != null) { return(ConvertStringValue(stringValue, targetType, version)); } else if (value is Int32) { return(ConvertInt32Value((int)value, targetType, primitiveTypeReference, readerBehavior == null ? false : readerBehavior.UseV1ProviderBehavior)); } else if (value is Double) { Double doubleValue = (Double)value; if (targetType == typeof(Single)) { return(Convert.ToSingle(doubleValue)); } if (!IsV1PrimitiveType(targetType) || (targetType != typeof(Double) && (readerBehavior == null || !readerBehavior.UseV1ProviderBehavior))) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.ODataFullName())); } } else if (value is bool) { if (targetType != typeof(bool) && (readerBehavior == null || readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.ODataFullName())); } } else if (value is DateTime) { return(ConvertDateTimeValue((DateTime)value, targetType, primitiveTypeReference, readerBehavior)); } else if (value is DateTimeOffset) { // Currently, we do not support any conversion for DateTimeOffset date type. Hence failing if the target // type is not DateTimeOffset. if (targetType != typeof(DateTimeOffset)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.ODataFullName())); } } } catch (Exception e) { if (!ExceptionUtils.IsCatchableExceptionType(e)) { throw; } throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, e); } // otherwise just return the value without doing any conversion return(value); }
/// <summary> /// Returns the corresponding edm type reference for the given edm type. /// </summary> /// <param name="edmType">EdmType instance.</param> /// <param name="isNullable">A boolean value indicating whether the clr type of this edm type is nullable</param> /// <returns>Returns the corresponding edm type reference for the given edm type.</returns> internal static IEdmTypeReference ToEdmTypeReference(this IEdmType edmType, bool isNullable) { return(EdmLibraryExtensions.ToTypeReference(edmType, isNullable)); }
/// <summary> /// Gets the type name based on the given odata value. /// </summary> /// <param name="value">The value.</param> /// <param name="model">The model used to handle unsigned int conversions.</param> /// <returns>The type name for the context URI.</returns> private static string GetTypeNameForValue(ODataValue value, IEdmModel model) { if (value == null) { return(null); } // special identifier for null values. if (value.IsNullValue) { return(ODataConstants.ContextUriFragmentNull); } var typeAnnotation = value.GetAnnotation <SerializationTypeNameAnnotation>(); if (typeAnnotation != null && !string.IsNullOrEmpty(typeAnnotation.TypeName)) { return(typeAnnotation.TypeName); } var complexValue = value as ODataComplexValue; if (complexValue != null) { return(complexValue.TypeName); } var collectionValue = value as ODataCollectionValue; if (collectionValue != null) { return(EdmLibraryExtensions.GetCollectionTypeFullName(collectionValue.TypeName)); } var enumValue = value as ODataEnumValue; if (enumValue != null) { return(enumValue.TypeName); } ODataPrimitiveValue primitive = value as ODataPrimitiveValue; if (primitive == null) { Debug.Assert(value is ODataStreamReferenceValue, "value is ODataStreamReferenceValue"); throw new ODataException(Strings.ODataContextUriBuilder_StreamValueMustBePropertiesOfODataEntry); } // Try convert to underlying type if the primitive value is unsigned int. IEdmTypeDefinitionReference typeDefinitionReference = model.ResolveUIntTypeDefinition(primitive.Value); if (typeDefinitionReference != null) { return(typeDefinitionReference.FullName()); } IEdmPrimitiveTypeReference primitiveValueTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(primitive.Value.GetType()); return(primitiveValueTypeReference == null ? null : primitiveValueTypeReference.FullName()); }
/// <summary> /// Converts the given JSON value to the expected type as per OData conversion rules for JSON values. /// </summary> /// <param name="value">Value to the converted.</param> /// <param name="primitiveTypeReference">Type reference to which the value needs to be converted.</param> /// <param name="messageReaderSettings">The message reader settings used for reading.</param> /// <param name="version">The version of the OData protocol used for reading.</param> /// <param name="validateNullValue">true to validate null values; otherwise false.</param> /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param> /// <returns>Object which is in sync with the property type (modulo the V1 exception of converting numbers to non-compatible target types).</returns> internal static object ConvertValue( object value, IEdmPrimitiveTypeReference primitiveTypeReference, ODataMessageReaderSettings messageReaderSettings, ODataVersion version, bool validateNullValue, string propertyName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(primitiveTypeReference != null, "primitiveTypeReference != null"); if (value == null) { // Only primitive type references are validated. Core model is sufficient. ReaderValidationUtils.ValidateNullValue( EdmCoreModel.Instance, primitiveTypeReference, messageReaderSettings, validateNullValue, version, propertyName); return(null); } try { Type targetType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false); string stringValue = value as string; if (stringValue != null) { return(ConvertStringValue(stringValue, targetType)); } else if (value is Int32) { return(ConvertInt32Value((int)value, targetType, primitiveTypeReference)); } else if (value is Double) { Double doubleValue = (Double)value; if (targetType == typeof(Single)) { return(Convert.ToSingle(doubleValue)); } if (targetType != typeof(Double)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.ODataFullName())); } } else if (value is bool) { if (targetType != typeof(bool)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.ODataFullName())); } } else if (value is DateTime) { if (targetType != typeof(DateTime)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.ODataFullName())); } } else if (value is DateTimeOffset) { if (targetType != typeof(DateTimeOffset)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.ODataFullName())); } } } catch (Exception e) { if (!ExceptionUtils.IsCatchableExceptionType(e)) { throw; } throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, e); } // otherwise just return the value without doing any conversion return(value); }
/// <summary> /// Constructor creating a new reader scope. /// </summary> /// <param name="state">The reader state of this scope.</param> /// <param name="name">The parameter name attached to this scope.</param> /// <param name="value">The parameter value attached to this scope.</param> public Scope(ODataParameterReaderState state, string name, object value) { Debug.Assert( state == ODataParameterReaderState.Start && name == null && value == null || state == ODataParameterReaderState.Value && !string.IsNullOrEmpty(name) && (value == null || value is ODataEnumValue || EdmLibraryExtensions.IsPrimitiveType(value.GetType())) || state == ODataParameterReaderState.Resource && !string.IsNullOrEmpty(name) && value == null || state == ODataParameterReaderState.ResourceSet && !string.IsNullOrEmpty(name) && value == null || state == ODataParameterReaderState.Collection && !string.IsNullOrEmpty(name) && value == null || state == ODataParameterReaderState.Exception && name == null && value == null || state == ODataParameterReaderState.Completed && name == null && value == null, "Reader state and associated item do not match."); this.state = state; this.name = name; this.value = value; }
public void IsPrimitiveTypeForUnsupportedTypesShouldBeFalse(Type type) { bool result = EdmLibraryExtensions.IsPrimitiveType(type); result.Should().BeFalse(); }
public void WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isTopLevel, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { Debug.Assert(complexValue != null, "complexValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire complex instance; // for top-level complex properties we already wrote the object scope (and the context URI when needed). if (!isTopLevel) { this.JsonWriter.StartObjectScope(); } string typeName = complexValue.TypeName; if (isTopLevel) { Debug.Assert(metadataTypeReference == null, "Never expect a metadata type for top-level properties."); if (typeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightValueSerializer_MissingTypeNameOnComplex); } } else { // In requests, we allow the property 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_NoExpectedTypeOrTypeNameSpecifiedForComplexValueRequest); } } // Resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata. IEdmComplexTypeReference complexValueTypeReference = (IEdmComplexTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType); Debug.Assert( metadataTypeReference == null || complexValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, complexValueTypeReference), "Complex property types must be the same as or inherit from the ones from metadata (unless open)."); typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, metadataTypeReference, complexValueTypeReference, isOpenPropertyType); if (typeName != null) { ODataJsonLightWriterUtils.WriteODataTypeInstanceAnnotation(this.JsonWriter, typeName); } // Write the properties of the complex value as usual. Note we do not allow complex types to contain named stream properties. this.PropertySerializer.WriteProperties( complexValueTypeReference == null ? null : complexValueTypeReference.ComplexDefinition(), complexValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null /*projectedProperties */); // End the object scope which represents the complex instance; // for top-level complex properties we already wrote the end object scope. if (!isTopLevel) { this.JsonWriter.EndObjectScope(); } this.DecreaseRecursionDepth(); }
/// <summary> /// Gets the specified type name as an EDM Collection type, e.g. Collection(Edm.String) /// </summary> /// <param name="itemTypeName">Type name of the items in the collection.</param> /// <returns>Collection type name for the specified item type name.</returns> private static string GetCollectionName(string itemTypeName) { return(itemTypeName == null ? null : EdmLibraryExtensions.GetCollectionTypeName(itemTypeName)); }
/// <summary> /// Verifies that the given <paramref name="primitiveValue"/> is or can be coerced to <paramref name="expectedTypeReference"/>, and coerces it if necessary. /// </summary> /// <param name="primitiveValue">An EDM primitive value to verify.</param> /// <param name="model">Model to verify against.</param> /// <param name="expectedTypeReference">Expected type reference.</param> /// <param name="version">The version to use for reading.</param> /// <returns>Coerced version of the <paramref name="primitiveValue"/>.</returns> internal static object VerifyAndCoerceUriPrimitiveLiteral(object primitiveValue, IEdmModel model, IEdmTypeReference expectedTypeReference, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); ExceptionUtils.CheckArgumentNotNull(primitiveValue, "primitiveValue"); ExceptionUtils.CheckArgumentNotNull(model, "model"); ExceptionUtils.CheckArgumentNotNull(expectedTypeReference, "expectedTypeReference"); // First deal with null literal ODataUriNullValue nullValue = primitiveValue as ODataUriNullValue; if (nullValue != null) { if (!expectedTypeReference.IsNullable) { throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralNullOnNonNullableType(expectedTypeReference.ODataFullName())); } // If it was just 'null' on the wire (no typed null) then just take what was expected and use that if (nullValue.TypeName == null) { nullValue.TypeName = expectedTypeReference.ODataFullName(); return(nullValue); } IEdmType actualResolvedType = TypeNameOracle.ResolveAndValidateTypeName(model, nullValue.TypeName, expectedTypeReference.Definition.TypeKind); Debug.Assert(actualResolvedType != null, "This is a primitive-only codepath so actualResolvedType != null."); if (actualResolvedType.IsSpatial()) { ODataVersionChecker.CheckSpatialValue(version); } if (TypePromotionUtils.CanConvertTo(actualResolvedType.ToTypeReference(), expectedTypeReference)) { nullValue.TypeName = expectedTypeReference.ODataFullName(); return(nullValue); } throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralNullTypeVerificationFailure(expectedTypeReference.ODataFullName(), nullValue.TypeName)); } // Only other positive case is a numeric primitive that needs to be coerced IEdmPrimitiveTypeReference expectedPrimitiveTypeReference = expectedTypeReference.AsPrimitiveOrNull(); if (expectedPrimitiveTypeReference == null) { throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralTypeVerificationFailure(expectedTypeReference.ODataFullName(), primitiveValue)); } object coercedResult = CoerceNumericType(primitiveValue, expectedPrimitiveTypeReference.PrimitiveDefinition()); if (coercedResult != null) { return(coercedResult); } Type actualType = primitiveValue.GetType(); Type targetType = TypeUtils.GetNonNullableType(EdmLibraryExtensions.GetPrimitiveClrType(expectedPrimitiveTypeReference)); // If target type is assignable from actual type, we're OK if (targetType.IsAssignableFrom(actualType)) { return(primitiveValue); } throw new ODataException(ODataErrorStrings.ODataUriUtils_ConvertFromUriLiteralTypeVerificationFailure(expectedPrimitiveTypeReference.ODataFullName(), primitiveValue)); }
public void CloneForNullShouldBeNull() { Assert.Null(EdmLibraryExtensions.Clone(null, false)); }
public void IsPrimitiveTypeForSupportedTypesShouldBeTrue(Type type) { bool result = EdmLibraryExtensions.IsPrimitiveType(type); Assert.True(result); }
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); }
protected void EnterScope(ODataParameterReaderState state, string name, object value) { if (((state == ODataParameterReaderState.Value) && (value != null)) && (!(value is ODataComplexValue) && !EdmLibraryExtensions.IsPrimitiveType(value.GetType()))) { throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.ODataParameterReaderCore_ValueMustBePrimitiveOrComplexOrNull)); } if ((this.scopes.Count == 0) || (this.State != ODataParameterReaderState.Exception)) { if (state == ODataParameterReaderState.Completed) { List <string> list = new List <string>(); foreach (IEdmFunctionParameter parameter in this.FunctionImport.Parameters.Skip <IEdmFunctionParameter>(this.FunctionImport.IsBindable ? 1 : 0)) { if (!this.parametersRead.Contains(parameter.Name)) { list.Add(parameter.Name); } } if (list.Count > 0) { this.scopes.Push(new Scope(ODataParameterReaderState.Exception, null, null)); throw new ODataException(Microsoft.Data.OData.Strings.ODataParameterReaderCore_ParametersMissingInPayload(this.FunctionImport.Name, string.Join(",", list.ToArray()))); } } this.scopes.Push(new Scope(state, name, value)); } }
/// <summary> /// Asynchronously writes out the value of a resource (complex or entity). /// </summary> /// <param name="resourceValue">The resource (complex or entity) value to write.</param> /// <param name="metadataTypeReference">The metadata type for the resource value.</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open (dynamic) property.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <remarks>The current recursion depth should be a value, measured by the number of resource and collection values between /// this resource value and the top-level payload, not including this one.</remarks> /// <returns>A task that represents the asynchronous write operation.</returns> public virtual async Task WriteResourceValueAsync( ODataResourceValue resourceValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, IDuplicatePropertyNameChecker duplicatePropertyNamesChecker) { Debug.Assert(resourceValue != null, "resourceValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire resource instance; await this.AsynchronousJsonWriter.StartObjectScopeAsync().ConfigureAwait(false); string typeName = resourceValue.TypeName; // In requests, we allow the property 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_NoExpectedTypeOrTypeNameSpecifiedForResourceValueRequest); } // Resolve the type name to the type; if no type name is specified we will use the type inferred from metadata. IEdmStructuredTypeReference resourceValueTypeReference = (IEdmStructuredTypeReference)TypeNameOracle.ResolveAndValidateTypeForResourceValue(this.Model, metadataTypeReference, resourceValue, isOpenPropertyType, this.WriterValidator); Debug.Assert( metadataTypeReference == null || resourceValueTypeReference == null || EdmLibraryExtensions.IsAssignableFrom(metadataTypeReference, resourceValueTypeReference), "Complex property types must be the same as or inherit from the ones from metadata (unless open)."); typeName = this.JsonLightOutputContext.TypeNameOracle.GetValueTypeNameForWriting(resourceValue, metadataTypeReference, resourceValueTypeReference, isOpenPropertyType); if (typeName != null) { await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName) .ConfigureAwait(false); } // Write custom instance annotations await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(resourceValue.InstanceAnnotations) .ConfigureAwait(false); // Write the properties of the resource value as usual. Note we do not allow resource types to contain named stream properties. await this.PropertySerializer.WritePropertiesAsync( resourceValueTypeReference == null?null : resourceValueTypeReference.StructuredDefinition(), resourceValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null).ConfigureAwait(false); // End the object scope which represents the resource instance; await this.AsynchronousJsonWriter.EndObjectScopeAsync().ConfigureAwait(false); this.DecreaseRecursionDepth(); }
public void AsCollectionOrNullForNonCollectionShouldBeNull() { IEdmCollectionTypeReference collectionTypeReference = EdmLibraryExtensions.AsCollectionOrNull((IEdmTypeReference)productTypeReference); collectionTypeReference.Should().BeNull(); }
internal static void ValidateIsExpectedPrimitiveType(object value, IEdmTypeReference expectedTypeReference) { IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference); }
public void CloneForNullShouldBeNull() { EdmLibraryExtensions.Clone(null, false).Should().BeNull(); }
/// <summary> /// Create a ConstantNode /// </summary> /// <param name="constantValue">This node's primitive value.</param> public ConstantNode(object constantValue) { this.constantValue = constantValue; this.typeReference = constantValue == null ? null : EdmLibraryExtensions.GetPrimitiveTypeReference(constantValue.GetType()); }
private static bool IsStringPayloadValueAndTypeBinary(object value, IEdmTypeReference edmTypeReference) { IEdmPrimitiveTypeReference actualTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); return(actualTypeReference.IsString() && edmTypeReference.IsBinary()); }
public Scope(ODataCollectionReaderState state, object item, bool isCollectionElementEmpty) { Debug.Assert( state == ODataCollectionReaderState.Start && item == null || state == ODataCollectionReaderState.CollectionStart && item is ODataCollectionStart || state == ODataCollectionReaderState.Value && (item == null || item is ODataComplexValue || EdmLibraryExtensions.IsPrimitiveType(item.GetType()) || item is ODataEnumValue) || state == ODataCollectionReaderState.CollectionEnd && item is ODataCollectionStart || state == ODataCollectionReaderState.Exception && item == null || state == ODataCollectionReaderState.Completed && item == null, "Reader state and associated item do not match."); this.state = state; this.item = item; this.isCollectionElementEmpty = isCollectionElementEmpty; if (this.isCollectionElementEmpty) { Debug.Assert(state == ODataCollectionReaderState.CollectionStart, "Expected state to be CollectionStart."); } }
/// <summary> /// Determines the resource set type name to write to the payload. /// </summary> /// <param name="expectedResourceTypeName">The expected resource type name of the items in the resource set.</param> /// <param name="resourceSet">The ODataResourceSet whose type is to be written.</param> /// <param name="isUndeclared">true if the resource set is for some undeclared property</param> /// <returns>Type name to write to the payload, or null if no type name should be written.</returns> internal override string GetResourceSetTypeNameForForWriting(string expectedResourceTypeName, ODataResourceSet resourceSet, bool isUndeclared) { Debug.Assert(resourceSet != null, "resourceSet != null"); if (resourceSet.TypeAnnotation != null) { return(resourceSet.TypeAnnotation.TypeName); } var expectedResourceSetTypeName = expectedResourceTypeName == null ? null : EdmLibraryExtensions.GetCollectionTypeName(expectedResourceTypeName); if (expectedResourceSetTypeName != resourceSet.TypeName || isUndeclared) { return(resourceSet.TypeName); } return(null); }
/// <summary> /// Gets the type name from the given <paramref name="value"/>. /// </summary> /// <param name="value">The value to get the type name from. This can be an ODataPrimitiveValue, an ODataCollectionValue or a Clr primitive object.</param> /// <returns>The type name for the given <paramref name="value"/>.</returns> protected static string GetTypeNameFromValue(object value) { Debug.Assert(value != null, "value != null"); ODataPrimitiveValue primitiveValue = value as ODataPrimitiveValue; if (primitiveValue != null) { // primitiveValueTypeReference == null means: the EDM type of the primitive value cannot be determined. // This could possibly be due to value being an unsigned int. // In this case, simply return null because: // - If the property is regular property, the type is not needed since service model knows its exact type. // - If the property is dynamic property, ODL does not support dynamic property containing unsigned int value // since we don't know its underlying type as well as how to serialize it. IEdmPrimitiveTypeReference primitiveValueTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(primitiveValue.Value.GetType()); return(primitiveValueTypeReference == null ? null : primitiveValueTypeReference.FullName()); } ODataEnumValue enumValue = value as ODataEnumValue; if (enumValue != null) { return(enumValue.TypeName); } ODataResourceValue resourceValue = value as ODataResourceValue; if (resourceValue != null) { return(resourceValue.TypeName); } ODataCollectionValue collectionValue = value as ODataCollectionValue; if (collectionValue != null) { return(EdmLibraryExtensions.GetCollectionTypeFullName(collectionValue.TypeName)); } ODataBinaryStreamValue binaryStreamValue = value as ODataBinaryStreamValue; if (binaryStreamValue != null) { return(EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Binary, true).FullName()); } ODataStreamReferenceValue streamValue = value as ODataStreamReferenceValue; if (streamValue != null) { return(EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Stream, true).FullName()); } IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (primitiveTypeReference == null) { throw new ODataException(Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } return(primitiveTypeReference.FullName()); }
/// <summary> /// Reads the start of a collection; this includes collection-level properties (e.g., the 'results' property) if the version permits it. /// </summary> /// <param name="collectionStartPropertyAndAnnotationCollector">The duplicate property names checker used to keep track of the properties and annotations /// in the collection wrapper object.</param> /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param> /// <param name="expectedItemTypeReference">The expected item type reference or null if none is expected.</param> /// <param name="actualItemTypeReference">The validated actual item type reference (if specified in the payload) or the expected item type reference.</param> /// <returns>An <see cref="ODataCollectionStart"/> representing the collection-level information. Currently this is only the name of the collection in ATOM.</returns> /// <remarks> /// Pre-Condition: Any: the start of a nested collection value; if this is not a 'StartArray' node this method will fail. /// JsonNodeType.Property: the first property of the collection wrapper object after the context URI. /// JsonNodeType.EndObject: when the collection wrapper object has no properties (other than the context URI). /// Post-Condition: JsonNodeType.StartArray: the start of the array of the collection items. /// </remarks> internal ODataCollectionStart ReadCollectionStart( PropertyAndAnnotationCollector collectionStartPropertyAndAnnotationCollector, bool isReadingNestedPayload, IEdmTypeReference expectedItemTypeReference, out IEdmTypeReference actualItemTypeReference) { this.JsonReader.AssertNotBuffering(); actualItemTypeReference = expectedItemTypeReference; ODataCollectionStart collectionStart = null; if (isReadingNestedPayload) { Debug.Assert(!this.JsonLightInputContext.ReadingResponse, "Nested collections are only supported in parameter payloads in requests."); collectionStart = new ODataCollectionStart { Name = null }; } else { while (this.JsonReader.NodeType == JsonNodeType.Property) { IEdmTypeReference actualItemTypeRef = expectedItemTypeReference; this.ProcessProperty( collectionStartPropertyAndAnnotationCollector, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { if (this.JsonReader.NodeType == JsonNodeType.Property) { // Read over property name this.JsonReader.Read(); } switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (!IsValidODataAnnotationOfCollection(propertyName)) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); } this.JsonReader.SkipValue(); break; case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithoutValue: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_TopLevelPropertyAnnotationWithoutProperty(propertyName)); case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0) { throw new ODataException( ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_InvalidTopLevelPropertyName(propertyName, JsonLightConstants.ODataValuePropertyName)); } string payloadTypeName = ValidateDataPropertyTypeNameAnnotation(collectionStartPropertyAndAnnotationCollector, propertyName); if (payloadTypeName != null) { string itemTypeName = EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); if (itemTypeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_InvalidCollectionTypeName(payloadTypeName)); } EdmTypeKind targetTypeKind; ODataTypeAnnotation typeAnnotation; Func <EdmTypeKind> typeKindFromPayloadFunc = () => { throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart_TypeKindFromPayloadFunc)); }; actualItemTypeRef = this.ReaderValidator.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*expectStructuredType*/ null, /*defaultPrimitivePayloadType*/ null, expectedItemTypeReference, itemTypeName, this.Model, typeKindFromPayloadFunc, out targetTypeKind, out typeAnnotation); } collectionStart = new ODataCollectionStart { Name = null }; break; case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart)); } }); actualItemTypeReference = actualItemTypeRef; } if (collectionStart == null) { // No collection property found; there should be exactly one property in the collection wrapper that does not have a reserved name. throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_ExpectedCollectionPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } } // at this point the reader is positioned on the start array node for the collection contents if (this.JsonReader.NodeType != JsonNodeType.StartArray) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionContentStart(this.JsonReader.NodeType)); } this.JsonReader.AssertNotBuffering(); return(collectionStart); }
/// <summary> /// Constructor creating a new reader scope. /// </summary> /// <param name="state">The reader state of this scope.</param> /// <param name="name">The parameter name attached to this scope.</param> /// <param name="value">The parameter value attached to this scope.</param> public Scope(ODataParameterReaderState state, string name, object value) { Debug.Assert( state == ODataParameterReaderState.Start && name == null && value == null || state == ODataParameterReaderState.Value && !string.IsNullOrEmpty(name) && (value == null || value is ODataComplexValue || EdmLibraryExtensions.IsPrimitiveType(value.GetType())) || #if SUPPORT_ENTITY_PARAMETER state == ODataParameterReaderState.Entry && !string.IsNullOrEmpty(name) && value == null || state == ODataParameterReaderState.Feed && !string.IsNullOrEmpty(name) && value == null || #endif state == ODataParameterReaderState.Collection && !string.IsNullOrEmpty(name) && value == null || state == ODataParameterReaderState.Exception && name == null && value == null || state == ODataParameterReaderState.Completed && name == null && value == null, "Reader state and associated item do not match."); this.state = state; this.name = name; this.value = value; }
/// <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); }
/// <summary> /// Converts the given payload value to the type defined in a type definition. /// </summary> /// <param name="value">The given payload value.</param> /// <param name="edmTypeReference">The expected type reference from model.</param> /// <returns>The converted value of the type.</returns> public virtual object ConvertFromPayloadValue(object value, IEdmTypeReference edmTypeReference) { IEdmPrimitiveTypeReference primitiveTypeReference = edmTypeReference as IEdmPrimitiveTypeReference; Debug.Assert(primitiveTypeReference != null, "primitiveTypeReference != null"); if (primitiveTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.PrimitiveType) { return(value); } try { Type targetType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false); string stringValue = value as string; if (stringValue != null) { return(ConvertStringValue(stringValue, targetType)); } else if (value is Int32) { return(ConvertInt32Value((int)value, targetType, primitiveTypeReference)); } else if (value is Decimal) { Decimal decimalValue = (Decimal)value; if (targetType == typeof(Int64)) { return(Convert.ToInt64(decimalValue)); } if (targetType == typeof(Double)) { return(Convert.ToDouble(decimalValue)); } if (targetType == typeof(Single)) { return(Convert.ToSingle(decimalValue)); } if (targetType != typeof(Decimal)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDecimal(primitiveTypeReference.FullName())); } } else if (value is Double) { Double doubleValue = (Double)value; if (targetType == typeof(Single)) { return(Convert.ToSingle(doubleValue)); } if (targetType != typeof(Double)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.FullName())); } } else if (value is bool) { if (targetType != typeof(bool)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.FullName())); } } else if (value is DateTime) { if (targetType != typeof(DateTime)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.FullName())); } } else if (value is DateTimeOffset) { if (targetType != typeof(DateTimeOffset)) { throw new ODataException(ODataErrorStrings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.FullName())); } } } catch (Exception e) { if (!ExceptionUtils.IsCatchableExceptionType(e)) { throw; } throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, e, value.ToString()); } // otherwise just return the value without doing any conversion return(value); }
/// <summary> /// Resolve a primitive value type name /// </summary> /// <param name="primitiveValue">The value to get the type name from.</param> /// <returns>A type for primitive value</returns> internal static IEdmTypeReference ResolveAndValidateTypeForPrimitiveValue(ODataPrimitiveValue primitiveValue) { return(EdmLibraryExtensions.GetPrimitiveTypeReference(primitiveValue.Value.GetType())); }
internal static object ConvertValue(object value, IEdmPrimitiveTypeReference primitiveTypeReference, ODataMessageReaderSettings messageReaderSettings, ODataVersion version, bool validateNullValue) { if (value == null) { ReaderValidationUtils.ValidateNullValue(EdmCoreModel.Instance, primitiveTypeReference, messageReaderSettings, validateNullValue, version); return(null); } try { Type primitiveClrType = EdmLibraryExtensions.GetPrimitiveClrType(primitiveTypeReference.PrimitiveDefinition(), false); ODataReaderBehavior readerBehavior = messageReaderSettings.ReaderBehavior; string stringValue = value as string; if (stringValue != null) { return(ConvertStringValue(stringValue, primitiveClrType, version)); } if (value is int) { return(ConvertInt32Value((int)value, primitiveClrType, primitiveTypeReference, (readerBehavior != null) && readerBehavior.UseV1ProviderBehavior)); } if (value is double) { double num = (double)value; if (primitiveClrType == typeof(float)) { return(Convert.ToSingle(num)); } if (!IsV1PrimitiveType(primitiveClrType) || ((primitiveClrType != typeof(double)) && ((readerBehavior == null) || !readerBehavior.UseV1ProviderBehavior))) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertDouble(primitiveTypeReference.ODataFullName())); } return(value); } if (value is bool) { if ((primitiveClrType != typeof(bool)) && ((readerBehavior == null) || (readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer))) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertBoolean(primitiveTypeReference.ODataFullName())); } return(value); } if (value is DateTime) { if ((primitiveClrType != typeof(DateTime)) && ((readerBehavior == null) || (readerBehavior.FormatBehaviorKind != ODataBehaviorKind.WcfDataServicesServer))) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertDateTime(primitiveTypeReference.ODataFullName())); } return(value); } if ((value is DateTimeOffset) && (primitiveClrType != typeof(DateTimeOffset))) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonReaderUtils_CannotConvertDateTimeOffset(primitiveTypeReference.ODataFullName())); } } catch (Exception exception) { if (!ExceptionUtils.IsCatchableExceptionType(exception)) { throw; } throw ReaderValidationUtils.GetPrimitiveTypeConversionException(primitiveTypeReference, exception); } return(value); }
/// <summary>Returns EDM name of the type for a collection of specified <paramref name="itemType"/>.</summary> /// <param name="itemType">Resource type of a single item in the collection.</param> /// <returns>EDM name of the type of a collection of <paramref name="itemType"/>.</returns> private static string GetName(ResourceType itemType) { Debug.Assert(itemType != null, "itemType != null"); return(EdmLibraryExtensions.GetCollectionTypeName(itemType.FullName)); }
/// <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)); }