JsonDeserializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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  for the specified stream.
        /// 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);
        }
 
        /// 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  for the specified stream.
        /// 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);
        }
 
        /// 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK