Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / JsonDeserializer.cs / 3 / JsonDeserializer.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides a deserializer for json content. // // // @owner [....] //--------------------------------------------------------------------- namespace System.Data.Services.Serializers { using System; using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Data.Services.Providers; ////// Provides a deserializer for json content. /// internal class JsonDeserializer : Deserializer { ///json reader which reads json content private readonly JsonReader jsonReader; ///Initializes a new /// input stream reader from which json content must be read. /// indicates whether this is a update operation or not /// Data service for which the deserializer will act. /// Tracker to use for modifications. internal JsonDeserializer(StreamReader request, bool update, IDataService dataService, UpdateTracker tracker) : base(update, dataService, tracker) { Debug.Assert(request != null, "request != null"); this.jsonReader = new JsonReader(request); } ///for the specified stream. returns the content format for the deserializer protected override ContentFormat ContentFormat { get { return ContentFormat.Json; } } ////// Converts the given value to the expected type as per json reader rules /// Make sure these rules are in [....] with jsonwriter. /// /// value to the converted /// name of the property whose value is getting converted /// clr type to which the value needs to be converted to ///object which is in [....] with the properties type internal static object ConvertValues(object value, string propertyName, Type typeToBeConverted) { if (value == null) { return null; } Type propertyType = Nullable.GetUnderlyingType(typeToBeConverted) ?? typeToBeConverted; try { string stringValue = value as string; if (stringValue != null) { if (propertyType == typeof(byte[])) { return Convert.FromBase64String(stringValue); } else if (propertyType == typeof(System.Data.Linq.Binary)) { return new System.Data.Linq.Binary(Convert.FromBase64String(stringValue)); } else if (propertyType == typeof(System.Xml.Linq.XElement)) { return System.Xml.Linq.XElement.Parse(stringValue, System.Xml.Linq.LoadOptions.PreserveWhitespace); } else if (propertyType == typeof(Guid)) { return new Guid(stringValue); } else { // For string types, we support conversion to all possible primitive types return Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture); } } else if (value is Int32) { int intValue = (int)value; if (propertyType == typeof(Int16)) { return Convert.ToInt16(intValue); } else if (propertyType == typeof(Byte)) { return Convert.ToByte(intValue); } else if (propertyType == typeof(SByte)) { return Convert.ToSByte(intValue); } else if (propertyType == typeof(Single)) { return Convert.ToSingle(intValue); } else if (propertyType == typeof(Double)) { return Convert.ToDouble(intValue); } else if (propertyType == typeof(Decimal) || propertyType == typeof(Int64)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingNumericValues(propertyName)); } } else if (value is Double) { Double doubleValue = (Double)value; if (propertyType == typeof(Single)) { return Convert.ToSingle(doubleValue); } } } catch (Exception e) { if (WebUtil.IsCatchableExceptionType(e)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingPropertyValue(propertyName, propertyType.Name), e); } } // otherwise just return the value without doing any conversion return value; } ////// Assumes the payload to represent a single object and processes accordingly /// /// info about the object being created ///the newly formed object that the payload represents protected override object CreateSingleObject(SegmentInfo segmentInfo) { object jsonObject = this.jsonReader.ReadValue(); bool existingRelationship; return this.CreateObject(jsonObject, segmentInfo, true /*topLevel*/, out existingRelationship); } ///Provides an opportunity to clean-up resources. /// /// Whether the call is being made from an explicit call to /// IDisposable.Dispose() rather than through the finalizer. /// protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { this.jsonReader.Dispose(); } } ////// Get the resource referred by the uri in the payload /// ///resource referred by the uri in the payload. protected override string GetLinkUriFromPayload() { object jsonObject = this.jsonReader.ReadValue(); // Otherwise top level json content must be Hashtable, since we don't allow multiple inserts // at the top level Hashtable jsonObjectHashtable = jsonObject as Hashtable; if (jsonObjectHashtable == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } string uri = ReadUri(jsonObjectHashtable); if (String.IsNullOrEmpty(uri)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MissingUriForLinkOperation); } return uri; } ////// Gets the array list object /// /// object representing json array ///strongly type array list object that json object represents private static ArrayList GetArrayList(object jsonObject) { ArrayList arrayList = jsonObject as ArrayList; if (arrayList == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceSetPropertyMustBeArray); } return arrayList; } ////// Verifies if the given element value is a deferred element or not /// /// element value ///true if this value is a deferred content else returns false private static bool IsDeferredElement(object element) { Hashtable hashtable = element as Hashtable; if (hashtable != null && hashtable.Count == 1 && hashtable[XmlConstants.JsonDeferredString] != null) { return true; } return false; } ////// Returns true if the payload is correct for the top level non-entity target. /// /// json object representing the data in the payload. /// information about the last segment in the request uri. /// resource object as specified in the payload. ///returns true if the payload is correct for non-entity resource. private static bool HandleTopLevelNonEntityProperty(Hashtable jsonObject, SegmentInfo segment, out object resource) { Debug.Assert(jsonObject != null, "jsonObject != null"); Debug.Assert(segment != null, "segment != null"); resource = null; if (segment.TargetKind == RequestTargetKind.Primitive || #if ASTORIA_OPEN_OBJECT segment.TargetKind == RequestTargetKind.OpenProperty || #endif segment.TargetKind == RequestTargetKind.ComplexObject) { if (jsonObject.Count == 1 && jsonObject.ContainsKey(segment.Identifier)) { // For open property, assume it to be a primitive or complex payload. // If its targeting an entity, then the type must be specified resource = jsonObject[segment.Identifier]; return true; } else #if ASTORIA_OPEN_OBJECT if (segment.TargetKind != RequestTargetKind.OpenProperty) #endif { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } } // its an entity resource payload return false; } ////// Read the uri from the json object /// /// metadata object which contains the uri. ///returns the uri as specified in the object. private static string ReadUri(Hashtable metadata) { string uri = null; // Get the uri for the metadata element object uriObject = metadata[XmlConstants.JsonUriString]; if (uriObject != null) { uri = uriObject as string; if (uri == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidUriMetadata); } } return uri; } ////// Create the object given the list of the properties. One of the properties will be __metadata property /// which will contain type information /// /// list of the properties and values specified in the payload /// info about the object being created /// true if the current object is a top level one, otherwise false /// does this resource already binded to its parent ///instance of the object created private object CreateObject(object jsonObject, SegmentInfo segmentInfo, bool topLevel, out bool existingRelationship) { this.RecurseEnter(); existingRelationship = true; bool existingResource = true; object resource = null; ResourceType resourceType; Hashtable jsonObjectHashtable; if (topLevel) { // Every top level json content must be Hashtable - primitive, complex or entity jsonObjectHashtable = jsonObject as Hashtable; if (jsonObjectHashtable == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } object nonEntityResource; if (HandleTopLevelNonEntityProperty(jsonObjectHashtable, segmentInfo, out nonEntityResource)) { // if the segment refers to primitive type, then return the value if (segmentInfo.TargetKind == RequestTargetKind.Primitive || #if ASTORIA_OPEN_OBJECT (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(nonEntityResource.GetType())) || #endif nonEntityResource == null) { return nonEntityResource; } jsonObject = nonEntityResource; } } else if ( jsonObject == null || #if ASTORIA_OPEN_OBJECT (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(jsonObject.GetType())) || #endif segmentInfo.TargetKind == RequestTargetKind.Primitive) { // For reference properties, we do not know if there was already some relationship setup // By setting it to null, we are unbinding the old relationship and hence existing relationship // is false // For open properties, if its null, there is no way we will be able to deduce the type existingRelationship = false; return jsonObject; } // Otherwise top level json content must be Hashtable, since we don't allow multiple inserts // at the top level jsonObjectHashtable = jsonObject as Hashtable; if (jsonObjectHashtable == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } ResourceType targetResourceType = null; #if ASTORIA_OPEN_OBJECT if (segmentInfo.TargetKind != RequestTargetKind.OpenProperty) #endif { targetResourceType = this.Service.Provider.GetResourceType(segmentInfo.TargetElementType); Debug.Assert(targetResourceType != null, "Should be able to resolve type for well known segments"); Debug.Assert( targetResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType || targetResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "targetType must be entity type or complex type"); } // Get the type and uri from the metadata element, if specified string uri; bool metadataElementSpecified; resourceType = this.GetTypeAndUriFromMetadata( jsonObjectHashtable, targetResourceType, topLevel, out uri, out metadataElementSpecified); Debug.Assert((resourceType != null && resourceType.ResourceTypeKind != ResourceTypeKind.Primitive) || uri != null, "Either uri or resource type must be specified"); this.CheckAndIncrementObjectCount(); if ((resourceType != null && resourceType.ResourceTypeKind != ResourceTypeKind.ComplexType) || uri != null) { // For inserts/updates, its okay not to specify anything in the payload. // Someone might just want to create a entity with default values or // merge nothing or replace everything with default values. if (this.Update) { if (!topLevel) { if (metadataElementSpecified && jsonObjectHashtable.Count > 1 || !metadataElementSpecified) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepUpdateNotSupported); } else if (uri == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_UriMissingForUpdateForDeepUpdates); } } if (topLevel) { // Checking for merge vs replace semantics // Only checking for top level resource entity // since we don't support update of deep resources resource = CreateObjectFromUri( resourceType, segmentInfo, (RequestDescription)null, true /*checkETag*/, true /*checkForNull*/, this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT /*replaceResource*/); } else { // case of binding at the first level. existingRelationship = false; return CreateObjectFromUri(resourceType, segmentInfo, uri, false /*checkETag*/, false /*checkForNull*/); } } else { // For insert, its a new resource that is getting created or an existing resource // getting binded. Either case, its a new relationship. existingRelationship = false; // For POST operations, the following rules holds true: // 1> If the uri is specified for navigation properties and no other property is specified, then its a bind operation. // Otherwise, ignore the uri and insert the new resource. if (uri != null) { if (segmentInfo.TargetSource == RequestTargetSource.Property && jsonObjectHashtable.Count == 1) { this.RecurseLeave(); return CreateObjectFromUri(resourceType, null, uri, false /*checkETag*/, false /*checkForNull*/); } } } } Debug.Assert(resourceType != null, "resourceType != null"); if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { Debug.Assert(resource == null, "resource == null"); resource = this.Service.Provider.CreateResource(null, resourceType.FullName); existingResource = false; } else if (!this.Update) { Debug.Assert(resource == null, "resource == null"); if (segmentInfo.TargetKind == RequestTargetKind.Resource) { // check for append rights whenever we need to create a resource this.Service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend); resource = this.Service.Provider.CreateResource(segmentInfo.TargetContainer.Name, resourceType.FullName); this.Tracker.TrackAction(resource, segmentInfo.TargetContainer, UpdateOperations.Add); } #if ASTORIA_OPEN_OBJECT else { Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.OpenProperty, "segmentInfo.TargetKind == RequestTargetKind.OpenProperty"); ResourceContainer container = this.Service.Provider.GetContainerForResourceType(resourceType.Type); resource = this.Service.Provider.CreateResource(container.Name, resourceType.FullName); } #endif existingResource = false; } bool changed = this.PopulateProperties(jsonObjectHashtable, resource, resourceType); // For put operations, you need not specify any property and that means reset all the properties. // hence for put operations, change is always true. changed = changed || this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT; if (changed && existingResource && segmentInfo.TargetContainer != null) { this.Tracker.TrackAction(resource, segmentInfo.TargetContainer, UpdateOperations.Change); } this.RecurseLeave(); return resource; } ////// Populate the properties of the given resource /// /// hashtable containing property name and values /// instance of the resource whose properties needs to be populated /// resource type whose properties needs to be populated ///true if any properties were set; false otherwise. private bool PopulateProperties(Hashtable jsonObject, object resource, ResourceType parentResourceType) { // Update all the properties specified in the payload. // Don't touch the properties which are not specified. Its upto the provider to interpret // the meaning of things which are not specified bool changed = false; foreach (string propertyName in jsonObject.Keys) { // Ignore the metadata property if (propertyName == XmlConstants.JsonMetadataString) { continue; } // Check if the property exists and try and set the value ResourceProperty resourceProperty = parentResourceType.TryResolvePropertyName(propertyName); if ( #if ASTORIA_OPEN_OBJECT parentResourceType.OpenTypeKind != OpenTypeKind.CompletelyOpen && #endif resourceProperty == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(propertyName, parentResourceType.FullName)); } // Get the property value, set it appropriately, and mark the object as changed. object propertyValue = jsonObject[propertyName]; bool existingRelationship; // If its a open property if (resourceProperty == null) { #if ASTORIA_OPEN_OBJECT this.HandleOpenTypeProperties(resource, propertyName, propertyValue); #endif changed = true; } else if (resourceProperty.TypeKind == ResourceTypeKind.ComplexType) { SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, true /* singleResult */); segmentInfo.TargetKind = RequestTargetKind.ComplexObject; propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship); SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service.Provider); changed = true; } else if (resourceProperty.TypeKind == ResourceTypeKind.Primitive) { // Ignoring the value of key properties in PUT payload if (!this.Update || !resourceProperty.IsOfKind(ResourcePropertyKind.Key)) { SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service.Provider); } changed = true; } else { Debug.Assert(ResourceTypeKind.EntityType == resourceProperty.TypeKind, "only expecting nav properties"); if (IsDeferredElement(propertyValue)) { // Skip the deferred element continue; } else { Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb); if (resourceProperty.Kind == ResourcePropertyKind.ResourceReference) { SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, true /* singleResult */); segmentInfo.TargetKind = RequestTargetKind.Resource; // For navigation property, allow both inserts and binding in this case propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship); if (!existingRelationship) { this.Service.Provider.SetReference(resource, resourceProperty.Name, propertyValue); } changed = true; } else if (resourceProperty.Kind == ResourcePropertyKind.ResourceSetReference) { if (propertyValue == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName)); } ArrayList resourceCollection = GetArrayList(propertyValue); SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, true /* singleResult */); foreach (object resourceObject in resourceCollection) { object resourceInstance = this.CreateObject(resourceObject, segmentInfo, false /*topLevel*/, out existingRelationship); Debug.Assert(resourceInstance != null, "resourceInstance != null"); if (!existingRelationship) { this.Service.Provider.AddReferenceToCollection(resource, resourceProperty.Name, resourceInstance); } changed = true; } } } } } return changed; } #if ASTORIA_OPEN_OBJECT ////// Handle the open type property /// /// parent resource to which the open property belongs to /// name of the property /// value of the property private void HandleOpenTypeProperties(object parentResource, string propertyName, object propertyValue) { bool existingRelationship; if (IsDeferredElement(propertyValue)) { // Skip the deferred element return; } // Check if its a collection or not ArrayList arrayList = propertyValue as ArrayList; if (arrayList == null) { SegmentInfo openPropertySegmentInfo = CreateSegment(null, propertyName, true /* singleResult */); propertyValue = this.CreateObject( propertyValue, openPropertySegmentInfo, false /*topLevel*/, out existingRelationship); // Resolve the type of the value if (propertyValue == null || WebUtil.IsPrimitiveType(propertyValue.GetType())) { // For open properties, just set the value since we only support primitive type properties as // open properties SetOpenPropertyValue(parentResource, propertyName, propertyValue, this.Service.Provider); } else { ResourceType openPropertyResourceType = this.Service.Provider.GetResourceType(propertyValue.GetType()); if (openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { this.Service.Provider.SetValue(parentResource, propertyName, propertyValue); } else { Debug.Assert(openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be of entity type"); Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb); if (!existingRelationship) { this.Service.Provider.SetReference(parentResource, propertyName, propertyValue); } } } } else { Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb); SegmentInfo openPropertySegmentInfo = CreateSegment(null, propertyName, false /* singleResult */); foreach (object openPropertyValue in arrayList) { propertyValue = this.CreateObject( openPropertyValue, openPropertySegmentInfo, false /*topLevel*/, out existingRelationship); if (propertyValue == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName)); } if (!existingRelationship) { this.Service.Provider.AddReferenceToCollection(parentResource, propertyName, propertyValue); } } } } #endif ////// Gets the type and uri specified in the metadata object in the given json object. /// /// jsonObject which contains the metadata information /// expected type that this segment of the uri is targeted to /// whether the segment represents the top level object. /// uri as specified in the metadata object. If its not specified, this is set to null /// returns true if the metadata element was specified ///typename and uri as specified in the metadata object private ResourceType GetTypeAndUriFromMetadata( Hashtable jsonObject, ResourceType expectedType, bool topLevel, out string uri, out bool metadataElementSpecified) { metadataElementSpecified = false; // Get the metadata object object metadataObject = jsonObject[XmlConstants.JsonMetadataString]; ResourceType targetType = expectedType; bool typeNameSpecified = false; uri = null; if (metadataObject != null) { metadataElementSpecified = true; Hashtable metadata = metadataObject as Hashtable; if (metadata == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidMetadataContent); } // Get the type information from the metadata object. if the type name is not specified, // then return the expectedType as the target type object objectTypeName = metadata[XmlConstants.JsonTypeString]; if (objectTypeName != null) { string typeName = objectTypeName as string; if (string.IsNullOrEmpty(typeName)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidTypeMetadata); } // Resolve resource type name targetType = this.Service.Provider.TryResolveTypeName(typeName); if (targetType == null || targetType.ResourceTypeKind == ResourceTypeKind.Primitive) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName)); } if (expectedType != null && !expectedType.Type.IsAssignableFrom(targetType.Type)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeSpecified(typeName, expectedType.FullName)); } typeNameSpecified = true; } uri = JsonDeserializer.ReadUri(metadata); } // Type information is optional for bind operations. // Top level operations cannot be bind operations, since uri need to have $links // for top level bind operations and that's a different code path. // For bind operations, uri must be specified and nothing else should be specified. bool bindOperation = !topLevel && uri != null && jsonObject.Count == 1; // type name must be specified for POST or PUT/MERGE operations. if (!typeNameSpecified) { if (!bindOperation) { if (expectedType == null) { // For open properties, you must specify the type information throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MissingTypeInformationForOpenTypeProperties); } else if (expectedType.HasDerivedTypes) { // For types that take part in inheritance, type information must be specified. throw DataServiceException.CreateBadRequestError(Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance); } } else { // If the type name is not specified, we should set the type name to null, since in case of inheritance, // we don't want to guess the type information. targetType = null; } } return targetType; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides a deserializer for json content. // // // @owner [....] //--------------------------------------------------------------------- namespace System.Data.Services.Serializers { using System; using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Data.Services.Providers; ////// Provides a deserializer for json content. /// internal class JsonDeserializer : Deserializer { ///json reader which reads json content private readonly JsonReader jsonReader; ///Initializes a new /// input stream reader from which json content must be read. /// indicates whether this is a update operation or not /// Data service for which the deserializer will act. /// Tracker to use for modifications. internal JsonDeserializer(StreamReader request, bool update, IDataService dataService, UpdateTracker tracker) : base(update, dataService, tracker) { Debug.Assert(request != null, "request != null"); this.jsonReader = new JsonReader(request); } ///for the specified stream. returns the content format for the deserializer protected override ContentFormat ContentFormat { get { return ContentFormat.Json; } } ////// Converts the given value to the expected type as per json reader rules /// Make sure these rules are in [....] with jsonwriter. /// /// value to the converted /// name of the property whose value is getting converted /// clr type to which the value needs to be converted to ///object which is in [....] with the properties type internal static object ConvertValues(object value, string propertyName, Type typeToBeConverted) { if (value == null) { return null; } Type propertyType = Nullable.GetUnderlyingType(typeToBeConverted) ?? typeToBeConverted; try { string stringValue = value as string; if (stringValue != null) { if (propertyType == typeof(byte[])) { return Convert.FromBase64String(stringValue); } else if (propertyType == typeof(System.Data.Linq.Binary)) { return new System.Data.Linq.Binary(Convert.FromBase64String(stringValue)); } else if (propertyType == typeof(System.Xml.Linq.XElement)) { return System.Xml.Linq.XElement.Parse(stringValue, System.Xml.Linq.LoadOptions.PreserveWhitespace); } else if (propertyType == typeof(Guid)) { return new Guid(stringValue); } else { // For string types, we support conversion to all possible primitive types return Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture); } } else if (value is Int32) { int intValue = (int)value; if (propertyType == typeof(Int16)) { return Convert.ToInt16(intValue); } else if (propertyType == typeof(Byte)) { return Convert.ToByte(intValue); } else if (propertyType == typeof(SByte)) { return Convert.ToSByte(intValue); } else if (propertyType == typeof(Single)) { return Convert.ToSingle(intValue); } else if (propertyType == typeof(Double)) { return Convert.ToDouble(intValue); } else if (propertyType == typeof(Decimal) || propertyType == typeof(Int64)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingNumericValues(propertyName)); } } else if (value is Double) { Double doubleValue = (Double)value; if (propertyType == typeof(Single)) { return Convert.ToSingle(doubleValue); } } } catch (Exception e) { if (WebUtil.IsCatchableExceptionType(e)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingPropertyValue(propertyName, propertyType.Name), e); } } // otherwise just return the value without doing any conversion return value; } ////// Assumes the payload to represent a single object and processes accordingly /// /// info about the object being created ///the newly formed object that the payload represents protected override object CreateSingleObject(SegmentInfo segmentInfo) { object jsonObject = this.jsonReader.ReadValue(); bool existingRelationship; return this.CreateObject(jsonObject, segmentInfo, true /*topLevel*/, out existingRelationship); } ///Provides an opportunity to clean-up resources. /// /// Whether the call is being made from an explicit call to /// IDisposable.Dispose() rather than through the finalizer. /// protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { this.jsonReader.Dispose(); } } ////// Get the resource referred by the uri in the payload /// ///resource referred by the uri in the payload. protected override string GetLinkUriFromPayload() { object jsonObject = this.jsonReader.ReadValue(); // Otherwise top level json content must be Hashtable, since we don't allow multiple inserts // at the top level Hashtable jsonObjectHashtable = jsonObject as Hashtable; if (jsonObjectHashtable == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } string uri = ReadUri(jsonObjectHashtable); if (String.IsNullOrEmpty(uri)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_MissingUriForLinkOperation); } return uri; } ////// Gets the array list object /// /// object representing json array ///strongly type array list object that json object represents private static ArrayList GetArrayList(object jsonObject) { ArrayList arrayList = jsonObject as ArrayList; if (arrayList == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ResourceSetPropertyMustBeArray); } return arrayList; } ////// Verifies if the given element value is a deferred element or not /// /// element value ///true if this value is a deferred content else returns false private static bool IsDeferredElement(object element) { Hashtable hashtable = element as Hashtable; if (hashtable != null && hashtable.Count == 1 && hashtable[XmlConstants.JsonDeferredString] != null) { return true; } return false; } ////// Returns true if the payload is correct for the top level non-entity target. /// /// json object representing the data in the payload. /// information about the last segment in the request uri. /// resource object as specified in the payload. ///returns true if the payload is correct for non-entity resource. private static bool HandleTopLevelNonEntityProperty(Hashtable jsonObject, SegmentInfo segment, out object resource) { Debug.Assert(jsonObject != null, "jsonObject != null"); Debug.Assert(segment != null, "segment != null"); resource = null; if (segment.TargetKind == RequestTargetKind.Primitive || #if ASTORIA_OPEN_OBJECT segment.TargetKind == RequestTargetKind.OpenProperty || #endif segment.TargetKind == RequestTargetKind.ComplexObject) { if (jsonObject.Count == 1 && jsonObject.ContainsKey(segment.Identifier)) { // For open property, assume it to be a primitive or complex payload. // If its targeting an entity, then the type must be specified resource = jsonObject[segment.Identifier]; return true; } else #if ASTORIA_OPEN_OBJECT if (segment.TargetKind != RequestTargetKind.OpenProperty) #endif { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } } // its an entity resource payload return false; } ////// Read the uri from the json object /// /// metadata object which contains the uri. ///returns the uri as specified in the object. private static string ReadUri(Hashtable metadata) { string uri = null; // Get the uri for the metadata element object uriObject = metadata[XmlConstants.JsonUriString]; if (uriObject != null) { uri = uriObject as string; if (uri == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidUriMetadata); } } return uri; } ////// Create the object given the list of the properties. One of the properties will be __metadata property /// which will contain type information /// /// list of the properties and values specified in the payload /// info about the object being created /// true if the current object is a top level one, otherwise false /// does this resource already binded to its parent ///instance of the object created private object CreateObject(object jsonObject, SegmentInfo segmentInfo, bool topLevel, out bool existingRelationship) { this.RecurseEnter(); existingRelationship = true; bool existingResource = true; object resource = null; ResourceType resourceType; Hashtable jsonObjectHashtable; if (topLevel) { // Every top level json content must be Hashtable - primitive, complex or entity jsonObjectHashtable = jsonObject as Hashtable; if (jsonObjectHashtable == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } object nonEntityResource; if (HandleTopLevelNonEntityProperty(jsonObjectHashtable, segmentInfo, out nonEntityResource)) { // if the segment refers to primitive type, then return the value if (segmentInfo.TargetKind == RequestTargetKind.Primitive || #if ASTORIA_OPEN_OBJECT (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(nonEntityResource.GetType())) || #endif nonEntityResource == null) { return nonEntityResource; } jsonObject = nonEntityResource; } } else if ( jsonObject == null || #if ASTORIA_OPEN_OBJECT (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(jsonObject.GetType())) || #endif segmentInfo.TargetKind == RequestTargetKind.Primitive) { // For reference properties, we do not know if there was already some relationship setup // By setting it to null, we are unbinding the old relationship and hence existing relationship // is false // For open properties, if its null, there is no way we will be able to deduce the type existingRelationship = false; return jsonObject; } // Otherwise top level json content must be Hashtable, since we don't allow multiple inserts // at the top level jsonObjectHashtable = jsonObject as Hashtable; if (jsonObjectHashtable == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity); } ResourceType targetResourceType = null; #if ASTORIA_OPEN_OBJECT if (segmentInfo.TargetKind != RequestTargetKind.OpenProperty) #endif { targetResourceType = this.Service.Provider.GetResourceType(segmentInfo.TargetElementType); Debug.Assert(targetResourceType != null, "Should be able to resolve type for well known segments"); Debug.Assert( targetResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType || targetResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "targetType must be entity type or complex type"); } // Get the type and uri from the metadata element, if specified string uri; bool metadataElementSpecified; resourceType = this.GetTypeAndUriFromMetadata( jsonObjectHashtable, targetResourceType, topLevel, out uri, out metadataElementSpecified); Debug.Assert((resourceType != null && resourceType.ResourceTypeKind != ResourceTypeKind.Primitive) || uri != null, "Either uri or resource type must be specified"); this.CheckAndIncrementObjectCount(); if ((resourceType != null && resourceType.ResourceTypeKind != ResourceTypeKind.ComplexType) || uri != null) { // For inserts/updates, its okay not to specify anything in the payload. // Someone might just want to create a entity with default values or // merge nothing or replace everything with default values. if (this.Update) { if (!topLevel) { if (metadataElementSpecified && jsonObjectHashtable.Count > 1 || !metadataElementSpecified) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepUpdateNotSupported); } else if (uri == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_UriMissingForUpdateForDeepUpdates); } } if (topLevel) { // Checking for merge vs replace semantics // Only checking for top level resource entity // since we don't support update of deep resources resource = CreateObjectFromUri( resourceType, segmentInfo, (RequestDescription)null, true /*checkETag*/, true /*checkForNull*/, this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT /*replaceResource*/); } else { // case of binding at the first level. existingRelationship = false; return CreateObjectFromUri(resourceType, segmentInfo, uri, false /*checkETag*/, false /*checkForNull*/); } } else { // For insert, its a new resource that is getting created or an existing resource // getting binded. Either case, its a new relationship. existingRelationship = false; // For POST operations, the following rules holds true: // 1> If the uri is specified for navigation properties and no other property is specified, then its a bind operation. // Otherwise, ignore the uri and insert the new resource. if (uri != null) { if (segmentInfo.TargetSource == RequestTargetSource.Property && jsonObjectHashtable.Count == 1) { this.RecurseLeave(); return CreateObjectFromUri(resourceType, null, uri, false /*checkETag*/, false /*checkForNull*/); } } } } Debug.Assert(resourceType != null, "resourceType != null"); if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { Debug.Assert(resource == null, "resource == null"); resource = this.Service.Provider.CreateResource(null, resourceType.FullName); existingResource = false; } else if (!this.Update) { Debug.Assert(resource == null, "resource == null"); if (segmentInfo.TargetKind == RequestTargetKind.Resource) { // check for append rights whenever we need to create a resource this.Service.Configuration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend); resource = this.Service.Provider.CreateResource(segmentInfo.TargetContainer.Name, resourceType.FullName); this.Tracker.TrackAction(resource, segmentInfo.TargetContainer, UpdateOperations.Add); } #if ASTORIA_OPEN_OBJECT else { Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.OpenProperty, "segmentInfo.TargetKind == RequestTargetKind.OpenProperty"); ResourceContainer container = this.Service.Provider.GetContainerForResourceType(resourceType.Type); resource = this.Service.Provider.CreateResource(container.Name, resourceType.FullName); } #endif existingResource = false; } bool changed = this.PopulateProperties(jsonObjectHashtable, resource, resourceType); // For put operations, you need not specify any property and that means reset all the properties. // hence for put operations, change is always true. changed = changed || this.Service.RequestParams.AstoriaHttpVerb == AstoriaVerbs.PUT; if (changed && existingResource && segmentInfo.TargetContainer != null) { this.Tracker.TrackAction(resource, segmentInfo.TargetContainer, UpdateOperations.Change); } this.RecurseLeave(); return resource; } ////// Populate the properties of the given resource /// /// hashtable containing property name and values /// instance of the resource whose properties needs to be populated /// resource type whose properties needs to be populated ///true if any properties were set; false otherwise. private bool PopulateProperties(Hashtable jsonObject, object resource, ResourceType parentResourceType) { // Update all the properties specified in the payload. // Don't touch the properties which are not specified. Its upto the provider to interpret // the meaning of things which are not specified bool changed = false; foreach (string propertyName in jsonObject.Keys) { // Ignore the metadata property if (propertyName == XmlConstants.JsonMetadataString) { continue; } // Check if the property exists and try and set the value ResourceProperty resourceProperty = parentResourceType.TryResolvePropertyName(propertyName); if ( #if ASTORIA_OPEN_OBJECT parentResourceType.OpenTypeKind != OpenTypeKind.CompletelyOpen && #endif resourceProperty == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(propertyName, parentResourceType.FullName)); } // Get the property value, set it appropriately, and mark the object as changed. object propertyValue = jsonObject[propertyName]; bool existingRelationship; // If its a open property if (resourceProperty == null) { #if ASTORIA_OPEN_OBJECT this.HandleOpenTypeProperties(resource, propertyName, propertyValue); #endif changed = true; } else if (resourceProperty.TypeKind == ResourceTypeKind.ComplexType) { SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, true /* singleResult */); segmentInfo.TargetKind = RequestTargetKind.ComplexObject; propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship); SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service.Provider); changed = true; } else if (resourceProperty.TypeKind == ResourceTypeKind.Primitive) { // Ignoring the value of key properties in PUT payload if (!this.Update || !resourceProperty.IsOfKind(ResourcePropertyKind.Key)) { SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service.Provider); } changed = true; } else { Debug.Assert(ResourceTypeKind.EntityType == resourceProperty.TypeKind, "only expecting nav properties"); if (IsDeferredElement(propertyValue)) { // Skip the deferred element continue; } else { Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb); if (resourceProperty.Kind == ResourcePropertyKind.ResourceReference) { SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, true /* singleResult */); segmentInfo.TargetKind = RequestTargetKind.Resource; // For navigation property, allow both inserts and binding in this case propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship); if (!existingRelationship) { this.Service.Provider.SetReference(resource, resourceProperty.Name, propertyValue); } changed = true; } else if (resourceProperty.Kind == ResourcePropertyKind.ResourceSetReference) { if (propertyValue == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName)); } ArrayList resourceCollection = GetArrayList(propertyValue); SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, true /* singleResult */); foreach (object resourceObject in resourceCollection) { object resourceInstance = this.CreateObject(resourceObject, segmentInfo, false /*topLevel*/, out existingRelationship); Debug.Assert(resourceInstance != null, "resourceInstance != null"); if (!existingRelationship) { this.Service.Provider.AddReferenceToCollection(resource, resourceProperty.Name, resourceInstance); } changed = true; } } } } } return changed; } #if ASTORIA_OPEN_OBJECT ////// Handle the open type property /// /// parent resource to which the open property belongs to /// name of the property /// value of the property private void HandleOpenTypeProperties(object parentResource, string propertyName, object propertyValue) { bool existingRelationship; if (IsDeferredElement(propertyValue)) { // Skip the deferred element return; } // Check if its a collection or not ArrayList arrayList = propertyValue as ArrayList; if (arrayList == null) { SegmentInfo openPropertySegmentInfo = CreateSegment(null, propertyName, true /* singleResult */); propertyValue = this.CreateObject( propertyValue, openPropertySegmentInfo, false /*topLevel*/, out existingRelationship); // Resolve the type of the value if (propertyValue == null || WebUtil.IsPrimitiveType(propertyValue.GetType())) { // For open properties, just set the value since we only support primitive type properties as // open properties SetOpenPropertyValue(parentResource, propertyName, propertyValue, this.Service.Provider); } else { ResourceType openPropertyResourceType = this.Service.Provider.GetResourceType(propertyValue.GetType()); if (openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) { this.Service.Provider.SetValue(parentResource, propertyName, propertyValue); } else { Debug.Assert(openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be of entity type"); Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb); if (!existingRelationship) { this.Service.Provider.SetReference(parentResource, propertyName, propertyValue); } } } } else { Deserializer.CheckForBindingInPutOperations(this.Service.RequestParams.AstoriaHttpVerb); SegmentInfo openPropertySegmentInfo = CreateSegment(null, propertyName, false /* singleResult */); foreach (object openPropertyValue in arrayList) { propertyValue = this.CreateObject( openPropertyValue, openPropertySegmentInfo, false /*topLevel*/, out existingRelationship); if (propertyValue == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(propertyName)); } if (!existingRelationship) { this.Service.Provider.AddReferenceToCollection(parentResource, propertyName, propertyValue); } } } } #endif ////// Gets the type and uri specified in the metadata object in the given json object. /// /// jsonObject which contains the metadata information /// expected type that this segment of the uri is targeted to /// whether the segment represents the top level object. /// uri as specified in the metadata object. If its not specified, this is set to null /// returns true if the metadata element was specified ///typename and uri as specified in the metadata object private ResourceType GetTypeAndUriFromMetadata( Hashtable jsonObject, ResourceType expectedType, bool topLevel, out string uri, out bool metadataElementSpecified) { metadataElementSpecified = false; // Get the metadata object object metadataObject = jsonObject[XmlConstants.JsonMetadataString]; ResourceType targetType = expectedType; bool typeNameSpecified = false; uri = null; if (metadataObject != null) { metadataElementSpecified = true; Hashtable metadata = metadataObject as Hashtable; if (metadata == null) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidMetadataContent); } // Get the type information from the metadata object. if the type name is not specified, // then return the expectedType as the target type object objectTypeName = metadata[XmlConstants.JsonTypeString]; if (objectTypeName != null) { string typeName = objectTypeName as string; if (string.IsNullOrEmpty(typeName)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidTypeMetadata); } // Resolve resource type name targetType = this.Service.Provider.TryResolveTypeName(typeName); if (targetType == null || targetType.ResourceTypeKind == ResourceTypeKind.Primitive) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName)); } if (expectedType != null && !expectedType.Type.IsAssignableFrom(targetType.Type)) { throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeSpecified(typeName, expectedType.FullName)); } typeNameSpecified = true; } uri = JsonDeserializer.ReadUri(metadata); } // Type information is optional for bind operations. // Top level operations cannot be bind operations, since uri need to have $links // for top level bind operations and that's a different code path. // For bind operations, uri must be specified and nothing else should be specified. bool bindOperation = !topLevel && uri != null && jsonObject.Count == 1; // type name must be specified for POST or PUT/MERGE operations. if (!typeNameSpecified) { if (!bindOperation) { if (expectedType == null) { // For open properties, you must specify the type information throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_MissingTypeInformationForOpenTypeProperties); } else if (expectedType.HasDerivedTypes) { // For types that take part in inheritance, type information must be specified. throw DataServiceException.CreateBadRequestError(Strings.BadRequest_TypeInformationMustBeSpecifiedForInhertiance); } } else { // If the type name is not specified, we should set the type name to null, since in case of inheritance, // we don't want to guess the type information. targetType = null; } } return targetType; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RC2.cs
- InvalidOleVariantTypeException.cs
- RpcCryptoContext.cs
- HierarchicalDataBoundControl.cs
- DataGridViewCellErrorTextNeededEventArgs.cs
- SmiEventSink.cs
- ProcessProtocolHandler.cs
- ConnectionManagementElementCollection.cs
- StructuralCache.cs
- Mutex.cs
- XmlDictionaryReader.cs
- GridItem.cs
- DataChangedEventManager.cs
- SemanticKeyElement.cs
- ValidationPropertyAttribute.cs
- SafeHandles.cs
- FixedDSBuilder.cs
- SerializationSectionGroup.cs
- AuthenticationServiceManager.cs
- WebPartsSection.cs
- BezierSegment.cs
- ServiceControllerDesigner.cs
- Screen.cs
- AnnotationService.cs
- CharEntityEncoderFallback.cs
- SecurityTokenSerializer.cs
- CollectionsUtil.cs
- DoubleLinkList.cs
- NativeObjectSecurity.cs
- WeakReadOnlyCollection.cs
- MissingSatelliteAssemblyException.cs
- NativeMethods.cs
- BoundColumn.cs
- WebConfigurationFileMap.cs
- SqlUserDefinedAggregateAttribute.cs
- StructuralObject.cs
- AdornedElementPlaceholder.cs
- ExecutionEngineException.cs
- IndependentAnimationStorage.cs
- XmlILTrace.cs
- TabItem.cs
- PrincipalPermission.cs
- CharEntityEncoderFallback.cs
- HtmlMeta.cs
- ObjectViewQueryResultData.cs
- Document.cs
- DataGridrowEditEndingEventArgs.cs
- DesignTimeVisibleAttribute.cs
- InputProviderSite.cs
- XmlChildEnumerator.cs
- CounterCreationDataConverter.cs
- DetailsViewDeletedEventArgs.cs
- TraceHandlerErrorFormatter.cs
- NavigationService.cs
- SerTrace.cs
- TextDecorationCollectionConverter.cs
- TraceFilter.cs
- HierarchicalDataBoundControl.cs
- InputLanguageCollection.cs
- DBAsyncResult.cs
- XmlDeclaration.cs
- StylusButton.cs
- WebControlToolBoxItem.cs
- DirectionalLight.cs
- InfoCardClaim.cs
- SafeWaitHandle.cs
- DBNull.cs
- FullTextBreakpoint.cs
- ImageSourceValueSerializer.cs
- CustomError.cs
- DataControlFieldCollection.cs
- XmlElement.cs
- SingleTagSectionHandler.cs
- EpmCustomContentWriterNodeData.cs
- InternalException.cs
- PerspectiveCamera.cs
- AccessKeyManager.cs
- GcSettings.cs
- NaturalLanguageHyphenator.cs
- InvalidOperationException.cs
- ProtocolElement.cs
- SimpleBitVector32.cs
- BindableTemplateBuilder.cs
- SystemUdpStatistics.cs
- EncoderNLS.cs
- CultureNotFoundException.cs
- IList.cs
- PaperSize.cs
- sqlser.cs
- FactoryMaker.cs
- XPathBinder.cs
- ListViewGroupConverter.cs
- ToolStripPanelRenderEventArgs.cs
- EndOfStreamException.cs
- ImageSourceValueSerializer.cs
- ControllableStoryboardAction.cs
- Helper.cs
- DbDataReader.cs
- BuildProvider.cs
- _NestedSingleAsyncResult.cs