JsonDeserializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Serializers / JsonDeserializer.cs / 1305376 / JsonDeserializer.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides a deserializer for json content.
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Serializers
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Text; 

    #endregion Namespaces. 
 
    /// 
    /// 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 from which JSON content must be read.
        /// Encoding to use for the stream. 
        /// indicates whether this is a update operation or not
        /// Data service for which the deserializer will act.
        /// Tracker to use for modifications.
        internal JsonDeserializer(Stream requestStream, Encoding encoding, bool update, IDataService dataService, UpdateTracker tracker) 
            : base(update, dataService, tracker)
        { 
            Debug.Assert(requestStream != null, "requestStream != null"); 

            // JsonReader is using StreamReader.Peek() method. However if the underlying stream does not support seeking 
            // StreamReader.Peek() will always return -1 what causes the JsonReader to think that all data has already been
            // read and the Json payload is invalid. We need to wrap non-seekable stream with BufferedStream to make it seekable.

            // Since in batch cases, we use our own implementation of Stream to read the batch content, we do not want to use BufferedStream in 
            // that case, since Peek just works fine in that case. Also, if we use BufferedStream, we get wierd behaviour since BufferedStream
            // tries to read few characters than the batchBoundary and our batch stream implementation does not handle that case well. 
            bool useGivenStream = requestStream.CanSeek || BatchStream.IsBatchStream(requestStream); 
            this.jsonReader = new JsonReader(new StreamReader(useGivenStream ? requestStream : new BufferedStream(requestStream), encoding));
        } 

        /// 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
        /// underlying data service provider. 
        /// object which is in [....] with the properties type
        internal static object ConvertValues(object value, string propertyName, Type typeToBeConverted, DataServiceProviderWrapper provider)
        {
            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 (propertyType != typeof(Int32) && !provider.IsV1Provider)
                    { 
                        // In V1, whenever we encountered a conversion which was unsafe, we would just return and most likely, it
                        // would fail when the provider tried and set the value to the property since the type won't match. 
                        // Ideally, we should have thrown here, instead of allowing it to pass to the provider. 
                        // Now in V2, with provider becoming public, and another configuration option available (EnableTypeConversion),
                        // it seems wrong to pass the value to the provider without doing the conversion when the EnableTypeConversion is set to true 
                        // But since we can't break the behaviour for V1 providers, we will be doing this only for custom V2 providers.
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingNumericValues(propertyName));
                    }
                } 
                else if (value is Double)
                { 
                    Double doubleValue = (Double)value; 
                    if (propertyType == typeof(Single))
                    { 
                        return Convert.ToSingle(doubleValue);
                    }
                    else if (propertyType != typeof(Double) && !provider.IsV1Provider)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingNumericValues(propertyName));
                    } 
                } 
            }
            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); 

            // Do not dispose the stream reader, since we don't own the underlying stream. 
        } 

        ///  
        /// Get the resource referred by the uri in the payload
        /// 
        /// resource referred by the uri in the payload.
        protected override string GetLinkUriFromPayload() 
        {
            // top level json content must be JsonObjectRecords, since we don't allow multiple inserts 
            // at the top level 
            JsonReader.JsonObjectRecords jsonObject = this.jsonReader.ReadValue() as JsonReader.JsonObjectRecords;
            if (jsonObject == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity);
            }
 
            string uri = ReadUri(jsonObject.Entries);
            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)
        { 
            JsonReader.JsonObjectRecords records = element as JsonReader.JsonObjectRecords;

            if (records != null && records.Count == 1 && records.OrderedKeys[0] == XmlConstants.JsonDeferredString)
            { 
                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(JsonReader.JsonObjectRecords 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 || 
                segment.TargetKind == RequestTargetKind.OpenProperty || 
                segment.TargetKind == RequestTargetKind.ComplexObject)
            { 
                if (jsonObject.Count == 1 && jsonObject.Entries.TryGetValue(segment.Identifier, out resource))
                {
                    // For open property, assume it to be a primitive or complex payload.
                    // If its targeting an entity, then the type must be specified 
                    return true;
                } 
                else if (segment.TargetKind != RequestTargetKind.OpenProperty) 
                {
                    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(Dictionary metadata) 
        {
            string uri = null; 
 
            // Get the uri for the metadata element
            object uriObject; 
            if (metadata.TryGetValue(XmlConstants.JsonUriString, out uriObject))
            {
                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;
            JsonReader.JsonObjectRecords jsonObjectRecord;
 
            if (topLevel)
            { 
                // Every top level json content must be JsonObjectRecords - primitive, complex or entity 
                jsonObjectRecord = jsonObject as JsonReader.JsonObjectRecords;
                if (jsonObjectRecord == null) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity);
                }
 
                object nonEntityResource;
                if (HandleTopLevelNonEntityProperty(jsonObjectRecord, segmentInfo, out nonEntityResource)) 
                { 
                    // if the segment refers to primitive type, then return the value
                    if (segmentInfo.TargetKind == RequestTargetKind.Primitive || 
                        nonEntityResource == null ||
                        (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(nonEntityResource.GetType())))
                    {
                        return nonEntityResource; 
                    }
 
                    jsonObject = nonEntityResource; 
                }
            } 
            else if (
                jsonObject == null ||
                (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(jsonObject.GetType())) ||
                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 JsonObjectRecords, since we don't allow multiple inserts
            // at the top level 
            jsonObjectRecord = jsonObject as JsonReader.JsonObjectRecords; 
            if (jsonObjectRecord == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity);
            }

            ResourceType targetResourceType = null; 
            if (segmentInfo.TargetKind != RequestTargetKind.OpenProperty)
            { 
                targetResourceType = segmentInfo.TargetResourceType; 
                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(
                jsonObjectRecord.Entries, 
                targetResourceType,
                topLevel,
                out uri,
                out metadataElementSpecified); 

            Debug.Assert((resourceType != null && resourceType.ResourceTypeKind != ResourceTypeKind.Primitive) || uri != null, "Either uri or resource type must be specified"); 
 
            if ((uri != null || resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) && segmentInfo.TargetKind == RequestTargetKind.OpenProperty)
            { 
                // Open navigation properties are not supported on OpenTypes.
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier));
            }
 
            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 && jsonObjectRecord.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 = GetObjectFromSegmentInfo(
                            resourceType, 
                            segmentInfo, 
                            true /*checkETag*/,
                            true /*checkForNull*/, 
                            this.Service.OperationContext.Host.AstoriaHttpVerb == AstoriaVerbs.PUT /*replaceResource*/);
                    }
                    else
                    { 
                        // case of binding at the first level.
                        existingRelationship = false; 
                        return this.GetTargetResourceToBind(uri, false /*checkNull*/); 
                    }
                } 
                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 && jsonObjectRecord.Count == 1)
                        { 
                            this.RecurseLeave();
                            return this.GetTargetResourceToBind(uri, false /*checkNull*/); 
                        } 
                    }
                } 
            }

            Debug.Assert(resourceType != null, "resourceType != null");
            if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
            {
                Debug.Assert(resource == null, "resource == null"); 
                resource = this.Updatable.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 
                    DataServiceConfiguration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend); 

                    resource = this.Updatable.CreateResource(segmentInfo.TargetContainer.Name, resourceType.FullName); 

                    // If resourceType is FF mapped with KeepInContent=false and the response format is Atom, we need to raise the response DSV version
                    // Note that we only need to do this for POST since PUT responds with 204 and DSV=1.0
                    // 
                    // Errr, mismatching request and response formats don't meet the bar at this point, commenting out the fix...
                    // 
                    //// this.UpdateAndCheckEpmRequestResponseDSV(resourceType, topLevel); 

                    this.Tracker.TrackAction(resource, segmentInfo.TargetContainer, UpdateOperations.Add); 
                }
                else
                {
                    Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.OpenProperty, "segmentInfo.TargetKind == RequestTargetKind.OpenProperty"); 

                    // Open navigation properties are not supported on OpenTypes. 
                    throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier)); 
                }
 
                existingResource = false;
            }

            bool changed = this.PopulateProperties(jsonObjectRecord, resource, segmentInfo.TargetContainer, 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.OperationContext.Host.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
        /// 
        /// JsonObjectRecords containing property name and values
        /// instance of the resource whose properties needs to be populated 
        /// resource set where  belongs to
        /// resource type whose properties needs to be populated 
        /// true if any properties were set; false otherwise. 
        private bool PopulateProperties(JsonReader.JsonObjectRecords jsonObject, object resource, ResourceSetWrapper parentResourceSet, 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; 
            List navProperties = new List();
            List navPropertyValues = new List(); 
 
            #region Handle Non-Nav Properties
            foreach (string propertyName in jsonObject.OrderedKeys) 
            {
                // 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 (resourceProperty == null && parentResourceType.IsOpenType == false)
                {
                    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.Entries[propertyName]; 
                bool existingRelationship;
 
                // If its a open property
                if (resourceProperty == null)
                {
                    this.HandleOpenTypeProperties(resource, propertyName, propertyValue); 
                    changed = true;
                } 
                else if (resourceProperty.TypeKind == ResourceTypeKind.ComplexType) 
                {
                    SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, null, true /* singleResult */); 
                    segmentInfo.TargetKind = RequestTargetKind.ComplexObject;
                    propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship);
                    SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service);
                    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);
                    }
 
                    changed = true;
                } 
                else 
                {
                    Debug.Assert(ResourceTypeKind.EntityType == resourceProperty.TypeKind, "only expecting nav properties"); 

                    if (IsDeferredElement(propertyValue))
                    {
                        // Skip the deferred element 
                        continue;
                    } 
                    else 
                    {
                        navProperties.Add(resourceProperty); 
                        navPropertyValues.Add(propertyValue);
                    }
                }
            } 

            #endregion Non-Navigation Properties 
 
            #region Handle Navigation Properties
 
            Debug.Assert(navProperties.Count == navPropertyValues.Count, "nav properties and nav property values count must be the same");

            // The reason why we need to do this is so that we can gaurantee that the nav properties are getting set at the end.
            // This is nice, since we already do this in the atom deserializer. Hence its consistent. Second, we wanted to 
            // give a gaurantee that when FK and nav properties are specified in the payload, nav properties always win.
            for (int i = 0; i < navProperties.Count; i++) 
            { 
                this.HandleNavigationProperty(parentResourceSet, parentResourceType, resource, navProperties[i], navPropertyValues[i]);
                changed = true; 
            }

            #endregion Handle Navigation Properties
 
            return changed;
        } 
 
        /// 
        /// 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, null, 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); 
                }
                else 
                { 
                    ResourceType openPropertyResourceType = WebUtil.GetResourceType(this.Service.Provider, propertyValue);
                    if (openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
                    {
                        this.Updatable.SetValue(parentResource, propertyName, propertyValue);
                    }
                    else 
                    {
                        Debug.Assert(openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be of entity type"); 
 
                        // Open navigation properties are not supported on OpenTypes.
                        throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); 
                    }
                }
            }
            else 
            {
                // Open navigation properties are not supported on OpenTypes. 
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); 
            }
        } 

        /// 
        /// 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(
            Dictionary jsonObjectTable,
            ResourceType expectedType, 
            bool topLevel,
            out string uri, 
            out bool metadataElementSpecified) 
        {
            metadataElementSpecified = false; 

            // Get the metadata object
            object metadataObject;
            ResourceType targetType = expectedType; 
            bool typeNameSpecified = false;
            uri = null; 
 
            if (jsonObjectTable.TryGetValue(XmlConstants.JsonMetadataString, out metadataObject))
            { 
                metadataElementSpecified = true;
                JsonReader.JsonObjectRecords metadata = metadataObject as JsonReader.JsonObjectRecords;
                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;
                if (metadata.Entries.TryGetValue(XmlConstants.JsonTypeString, out objectTypeName))
                {
                    string typeName = objectTypeName as string; 
                    if (string.IsNullOrEmpty(typeName))
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidTypeMetadata); 
                    }
 
                    // Resolve resource type name
                    targetType = this.Service.Provider.TryResolveResourceType(typeName);
                    if (targetType == null || targetType.ResourceTypeKind == ResourceTypeKind.Primitive)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName));
                    } 
 
                    if (expectedType != null && !expectedType.IsAssignableFrom(targetType))
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeSpecified(typeName, expectedType.FullName));
                    }

                    typeNameSpecified = true; 
                }
 
                uri = JsonDeserializer.ReadUri(metadata.Entries); 
            }
 
            // 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 && jsonObjectTable.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 (this.Service.Provider.HasDerivedTypes(expectedType))
                    { 
                        // 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; 
        }
 
        ///  
        /// Handle the navigation properties as specified in the payload
        ///  
        /// resource set where  belongs to
        /// resource type declaring the navigation property.
        /// instance of the resource declaring the navigation property.
        /// resource property containing metadata about the navigation property. 
        /// value of the navigation property.
        private void HandleNavigationProperty(ResourceSetWrapper parentResourceSet, ResourceType parentResourceType, object resource, ResourceProperty resourceProperty, object propertyValue) 
        { 
            Debug.Assert(parentResourceSet != null, "parentResourceSet != null");
            Debug.Assert(parentResourceType != null, "parentResourceType != null"); 
            Debug.Assert(resourceProperty != null && resourceProperty.TypeKind == ResourceTypeKind.EntityType, "its must be a nav property");

            bool existingRelationship;
            Deserializer.CheckForBindingInPutOperations(this.Service.OperationContext.Host.AstoriaHttpVerb); 

            ResourceSetWrapper propertySet = this.Service.Provider.GetContainer(parentResourceSet, parentResourceType, resourceProperty); 
            if (propertySet == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(resourceProperty.Name, parentResourceType.FullName)); 
            }

            // FeatureVersion needs to be 2.0 if any of the property in the types contained in the resource set has KeepInContent false
            this.RequestDescription.UpdateAndCheckEpmFeatureVersion(propertySet, this.Service); 

            if (resourceProperty.Kind == ResourcePropertyKind.ResourceReference) 
            { 
                SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, propertySet, 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.Updatable.SetReference(resource, resourceProperty.Name, propertyValue); 
                } 
            }
            else if (resourceProperty.Kind == ResourcePropertyKind.ResourceSetReference) 
            {
                if (propertyValue == null)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(resourceProperty.Name)); 
                }
 
                ArrayList resourceCollection = GetArrayList(propertyValue); 
                SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, propertySet, 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.Updatable.AddReferenceToCollection(resource, resourceProperty.Name, resourceInstance); 
                    }
                } 
            }
        }
    }
} 

// 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
{
    #region Namespaces. 

    using System; 
    using System.Collections; 
    using System.Collections.Generic;
    using System.Data.Services.Providers; 
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Text; 

    #endregion Namespaces. 
 
    /// 
    /// 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 from which JSON content must be read.
        /// Encoding to use for the stream. 
        /// indicates whether this is a update operation or not
        /// Data service for which the deserializer will act.
        /// Tracker to use for modifications.
        internal JsonDeserializer(Stream requestStream, Encoding encoding, bool update, IDataService dataService, UpdateTracker tracker) 
            : base(update, dataService, tracker)
        { 
            Debug.Assert(requestStream != null, "requestStream != null"); 

            // JsonReader is using StreamReader.Peek() method. However if the underlying stream does not support seeking 
            // StreamReader.Peek() will always return -1 what causes the JsonReader to think that all data has already been
            // read and the Json payload is invalid. We need to wrap non-seekable stream with BufferedStream to make it seekable.

            // Since in batch cases, we use our own implementation of Stream to read the batch content, we do not want to use BufferedStream in 
            // that case, since Peek just works fine in that case. Also, if we use BufferedStream, we get wierd behaviour since BufferedStream
            // tries to read few characters than the batchBoundary and our batch stream implementation does not handle that case well. 
            bool useGivenStream = requestStream.CanSeek || BatchStream.IsBatchStream(requestStream); 
            this.jsonReader = new JsonReader(new StreamReader(useGivenStream ? requestStream : new BufferedStream(requestStream), encoding));
        } 

        /// 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
        /// underlying data service provider. 
        /// object which is in [....] with the properties type
        internal static object ConvertValues(object value, string propertyName, Type typeToBeConverted, DataServiceProviderWrapper provider)
        {
            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 (propertyType != typeof(Int32) && !provider.IsV1Provider)
                    { 
                        // In V1, whenever we encountered a conversion which was unsafe, we would just return and most likely, it
                        // would fail when the provider tried and set the value to the property since the type won't match. 
                        // Ideally, we should have thrown here, instead of allowing it to pass to the provider. 
                        // Now in V2, with provider becoming public, and another configuration option available (EnableTypeConversion),
                        // it seems wrong to pass the value to the provider without doing the conversion when the EnableTypeConversion is set to true 
                        // But since we can't break the behaviour for V1 providers, we will be doing this only for custom V2 providers.
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingNumericValues(propertyName));
                    }
                } 
                else if (value is Double)
                { 
                    Double doubleValue = (Double)value; 
                    if (propertyType == typeof(Single))
                    { 
                        return Convert.ToSingle(doubleValue);
                    }
                    else if (propertyType != typeof(Double) && !provider.IsV1Provider)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInConvertingNumericValues(propertyName));
                    } 
                } 
            }
            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); 

            // Do not dispose the stream reader, since we don't own the underlying stream. 
        } 

        ///  
        /// Get the resource referred by the uri in the payload
        /// 
        /// resource referred by the uri in the payload.
        protected override string GetLinkUriFromPayload() 
        {
            // top level json content must be JsonObjectRecords, since we don't allow multiple inserts 
            // at the top level 
            JsonReader.JsonObjectRecords jsonObject = this.jsonReader.ReadValue() as JsonReader.JsonObjectRecords;
            if (jsonObject == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity);
            }
 
            string uri = ReadUri(jsonObject.Entries);
            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)
        { 
            JsonReader.JsonObjectRecords records = element as JsonReader.JsonObjectRecords;

            if (records != null && records.Count == 1 && records.OrderedKeys[0] == XmlConstants.JsonDeferredString)
            { 
                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(JsonReader.JsonObjectRecords 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 || 
                segment.TargetKind == RequestTargetKind.OpenProperty || 
                segment.TargetKind == RequestTargetKind.ComplexObject)
            { 
                if (jsonObject.Count == 1 && jsonObject.Entries.TryGetValue(segment.Identifier, out resource))
                {
                    // For open property, assume it to be a primitive or complex payload.
                    // If its targeting an entity, then the type must be specified 
                    return true;
                } 
                else if (segment.TargetKind != RequestTargetKind.OpenProperty) 
                {
                    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(Dictionary metadata) 
        {
            string uri = null; 
 
            // Get the uri for the metadata element
            object uriObject; 
            if (metadata.TryGetValue(XmlConstants.JsonUriString, out uriObject))
            {
                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;
            JsonReader.JsonObjectRecords jsonObjectRecord;
 
            if (topLevel)
            { 
                // Every top level json content must be JsonObjectRecords - primitive, complex or entity 
                jsonObjectRecord = jsonObject as JsonReader.JsonObjectRecords;
                if (jsonObjectRecord == null) 
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity);
                }
 
                object nonEntityResource;
                if (HandleTopLevelNonEntityProperty(jsonObjectRecord, segmentInfo, out nonEntityResource)) 
                { 
                    // if the segment refers to primitive type, then return the value
                    if (segmentInfo.TargetKind == RequestTargetKind.Primitive || 
                        nonEntityResource == null ||
                        (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(nonEntityResource.GetType())))
                    {
                        return nonEntityResource; 
                    }
 
                    jsonObject = nonEntityResource; 
                }
            } 
            else if (
                jsonObject == null ||
                (segmentInfo.TargetKind == RequestTargetKind.OpenProperty && WebUtil.IsPrimitiveType(jsonObject.GetType())) ||
                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 JsonObjectRecords, since we don't allow multiple inserts
            // at the top level 
            jsonObjectRecord = jsonObject as JsonReader.JsonObjectRecords; 
            if (jsonObjectRecord == null)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidResourceEntity);
            }

            ResourceType targetResourceType = null; 
            if (segmentInfo.TargetKind != RequestTargetKind.OpenProperty)
            { 
                targetResourceType = segmentInfo.TargetResourceType; 
                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(
                jsonObjectRecord.Entries, 
                targetResourceType,
                topLevel,
                out uri,
                out metadataElementSpecified); 

            Debug.Assert((resourceType != null && resourceType.ResourceTypeKind != ResourceTypeKind.Primitive) || uri != null, "Either uri or resource type must be specified"); 
 
            if ((uri != null || resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) && segmentInfo.TargetKind == RequestTargetKind.OpenProperty)
            { 
                // Open navigation properties are not supported on OpenTypes.
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier));
            }
 
            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 && jsonObjectRecord.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 = GetObjectFromSegmentInfo(
                            resourceType, 
                            segmentInfo, 
                            true /*checkETag*/,
                            true /*checkForNull*/, 
                            this.Service.OperationContext.Host.AstoriaHttpVerb == AstoriaVerbs.PUT /*replaceResource*/);
                    }
                    else
                    { 
                        // case of binding at the first level.
                        existingRelationship = false; 
                        return this.GetTargetResourceToBind(uri, false /*checkNull*/); 
                    }
                } 
                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 && jsonObjectRecord.Count == 1)
                        { 
                            this.RecurseLeave();
                            return this.GetTargetResourceToBind(uri, false /*checkNull*/); 
                        } 
                    }
                } 
            }

            Debug.Assert(resourceType != null, "resourceType != null");
            if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
            {
                Debug.Assert(resource == null, "resource == null"); 
                resource = this.Updatable.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 
                    DataServiceConfiguration.CheckResourceRights(segmentInfo.TargetContainer, EntitySetRights.WriteAppend); 

                    resource = this.Updatable.CreateResource(segmentInfo.TargetContainer.Name, resourceType.FullName); 

                    // If resourceType is FF mapped with KeepInContent=false and the response format is Atom, we need to raise the response DSV version
                    // Note that we only need to do this for POST since PUT responds with 204 and DSV=1.0
                    // 
                    // Errr, mismatching request and response formats don't meet the bar at this point, commenting out the fix...
                    // 
                    //// this.UpdateAndCheckEpmRequestResponseDSV(resourceType, topLevel); 

                    this.Tracker.TrackAction(resource, segmentInfo.TargetContainer, UpdateOperations.Add); 
                }
                else
                {
                    Debug.Assert(segmentInfo.TargetKind == RequestTargetKind.OpenProperty, "segmentInfo.TargetKind == RequestTargetKind.OpenProperty"); 

                    // Open navigation properties are not supported on OpenTypes. 
                    throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(segmentInfo.Identifier)); 
                }
 
                existingResource = false;
            }

            bool changed = this.PopulateProperties(jsonObjectRecord, resource, segmentInfo.TargetContainer, 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.OperationContext.Host.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
        /// 
        /// JsonObjectRecords containing property name and values
        /// instance of the resource whose properties needs to be populated 
        /// resource set where  belongs to
        /// resource type whose properties needs to be populated 
        /// true if any properties were set; false otherwise. 
        private bool PopulateProperties(JsonReader.JsonObjectRecords jsonObject, object resource, ResourceSetWrapper parentResourceSet, 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; 
            List navProperties = new List();
            List navPropertyValues = new List(); 
 
            #region Handle Non-Nav Properties
            foreach (string propertyName in jsonObject.OrderedKeys) 
            {
                // 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 (resourceProperty == null && parentResourceType.IsOpenType == false)
                {
                    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.Entries[propertyName]; 
                bool existingRelationship;
 
                // If its a open property
                if (resourceProperty == null)
                {
                    this.HandleOpenTypeProperties(resource, propertyName, propertyValue); 
                    changed = true;
                } 
                else if (resourceProperty.TypeKind == ResourceTypeKind.ComplexType) 
                {
                    SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, null, true /* singleResult */); 
                    segmentInfo.TargetKind = RequestTargetKind.ComplexObject;
                    propertyValue = this.CreateObject(propertyValue, segmentInfo, false /*topLevel*/, out existingRelationship);
                    SetPropertyValue(resourceProperty, resource, propertyValue, ContentFormat.Json, this.Service);
                    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);
                    }
 
                    changed = true;
                } 
                else 
                {
                    Debug.Assert(ResourceTypeKind.EntityType == resourceProperty.TypeKind, "only expecting nav properties"); 

                    if (IsDeferredElement(propertyValue))
                    {
                        // Skip the deferred element 
                        continue;
                    } 
                    else 
                    {
                        navProperties.Add(resourceProperty); 
                        navPropertyValues.Add(propertyValue);
                    }
                }
            } 

            #endregion Non-Navigation Properties 
 
            #region Handle Navigation Properties
 
            Debug.Assert(navProperties.Count == navPropertyValues.Count, "nav properties and nav property values count must be the same");

            // The reason why we need to do this is so that we can gaurantee that the nav properties are getting set at the end.
            // This is nice, since we already do this in the atom deserializer. Hence its consistent. Second, we wanted to 
            // give a gaurantee that when FK and nav properties are specified in the payload, nav properties always win.
            for (int i = 0; i < navProperties.Count; i++) 
            { 
                this.HandleNavigationProperty(parentResourceSet, parentResourceType, resource, navProperties[i], navPropertyValues[i]);
                changed = true; 
            }

            #endregion Handle Navigation Properties
 
            return changed;
        } 
 
        /// 
        /// 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, null, 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); 
                }
                else 
                { 
                    ResourceType openPropertyResourceType = WebUtil.GetResourceType(this.Service.Provider, propertyValue);
                    if (openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
                    {
                        this.Updatable.SetValue(parentResource, propertyName, propertyValue);
                    }
                    else 
                    {
                        Debug.Assert(openPropertyResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resource must be of entity type"); 
 
                        // Open navigation properties are not supported on OpenTypes.
                        throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); 
                    }
                }
            }
            else 
            {
                // Open navigation properties are not supported on OpenTypes. 
                throw DataServiceException.CreateBadRequestError(Strings.OpenNavigationPropertiesNotSupportedOnOpenTypes(propertyName)); 
            }
        } 

        /// 
        /// 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(
            Dictionary jsonObjectTable,
            ResourceType expectedType, 
            bool topLevel,
            out string uri, 
            out bool metadataElementSpecified) 
        {
            metadataElementSpecified = false; 

            // Get the metadata object
            object metadataObject;
            ResourceType targetType = expectedType; 
            bool typeNameSpecified = false;
            uri = null; 
 
            if (jsonObjectTable.TryGetValue(XmlConstants.JsonMetadataString, out metadataObject))
            { 
                metadataElementSpecified = true;
                JsonReader.JsonObjectRecords metadata = metadataObject as JsonReader.JsonObjectRecords;
                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;
                if (metadata.Entries.TryGetValue(XmlConstants.JsonTypeString, out objectTypeName))
                {
                    string typeName = objectTypeName as string; 
                    if (string.IsNullOrEmpty(typeName))
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequestStream_InvalidTypeMetadata); 
                    }
 
                    // Resolve resource type name
                    targetType = this.Service.Provider.TryResolveResourceType(typeName);
                    if (targetType == null || targetType.ResourceTypeKind == ResourceTypeKind.Primitive)
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeName(typeName));
                    } 
 
                    if (expectedType != null && !expectedType.IsAssignableFrom(targetType))
                    { 
                        throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidTypeSpecified(typeName, expectedType.FullName));
                    }

                    typeNameSpecified = true; 
                }
 
                uri = JsonDeserializer.ReadUri(metadata.Entries); 
            }
 
            // 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 && jsonObjectTable.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 (this.Service.Provider.HasDerivedTypes(expectedType))
                    { 
                        // 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; 
        }
 
        ///  
        /// Handle the navigation properties as specified in the payload
        ///  
        /// resource set where  belongs to
        /// resource type declaring the navigation property.
        /// instance of the resource declaring the navigation property.
        /// resource property containing metadata about the navigation property. 
        /// value of the navigation property.
        private void HandleNavigationProperty(ResourceSetWrapper parentResourceSet, ResourceType parentResourceType, object resource, ResourceProperty resourceProperty, object propertyValue) 
        { 
            Debug.Assert(parentResourceSet != null, "parentResourceSet != null");
            Debug.Assert(parentResourceType != null, "parentResourceType != null"); 
            Debug.Assert(resourceProperty != null && resourceProperty.TypeKind == ResourceTypeKind.EntityType, "its must be a nav property");

            bool existingRelationship;
            Deserializer.CheckForBindingInPutOperations(this.Service.OperationContext.Host.AstoriaHttpVerb); 

            ResourceSetWrapper propertySet = this.Service.Provider.GetContainer(parentResourceSet, parentResourceType, resourceProperty); 
            if (propertySet == null) 
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(resourceProperty.Name, parentResourceType.FullName)); 
            }

            // FeatureVersion needs to be 2.0 if any of the property in the types contained in the resource set has KeepInContent false
            this.RequestDescription.UpdateAndCheckEpmFeatureVersion(propertySet, this.Service); 

            if (resourceProperty.Kind == ResourcePropertyKind.ResourceReference) 
            { 
                SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, propertySet, 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.Updatable.SetReference(resource, resourceProperty.Name, propertyValue); 
                } 
            }
            else if (resourceProperty.Kind == ResourcePropertyKind.ResourceSetReference) 
            {
                if (propertyValue == null)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_CannotSetCollectionsToNull(resourceProperty.Name)); 
                }
 
                ArrayList resourceCollection = GetArrayList(propertyValue); 
                SegmentInfo segmentInfo = CreateSegment(resourceProperty, resourceProperty.Name, propertySet, 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.Updatable.AddReferenceToCollection(resource, resourceProperty.Name, resourceInstance); 
                    }
                } 
            }
        }
    }
} 

// 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