/// <summary> /// Returns true if the payload is correct for the top level non-entity target. /// </summary> /// <param name="jsonObject">json object representing the data in the payload.</param> /// <param name="segment">information about the last segment in the request uri.</param> /// <param name="resource">resource object as specified in the payload.</param> /// <returns>returns true if the payload is correct for non-entity resource.</returns> 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); }
/// <summary> /// Verifies if the given element value is a deferred element or not /// </summary> /// <param name="element">element value</param> /// <returns>true if this value is a deferred content else returns false</returns> 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); }
/// <summary> /// Get the resource referred by the uri in the payload /// </summary> /// <returns>resource referred by the uri in the payload.</returns> 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); }
/// <summary> /// Gets the type and uri specified in the metadata object in the given json object. /// </summary> /// <param name="jsonObjectTable">jsonObject which contains the metadata information</param> /// <param name="expectedType">expected type that this segment of the uri is targeted to</param> /// <param name="topLevel">whether the segment represents the top level object.</param> /// <param name="uri">uri as specified in the metadata object. If its not specified, this is set to null</param> /// <param name="metadataElementSpecified">returns true if the metadata element was specified</param> /// <returns>typename and uri as specified in the metadata object</returns> private ResourceType GetTypeAndUriFromMetadata( Dictionary <String, Object> 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); }
/// <summary> /// Populate the properties of the given resource /// </summary> /// <param name="jsonObject">JsonObjectRecords containing property name and values</param> /// <param name="resource">instance of the resource whose properties needs to be populated</param> /// <param name="parentResourceSet">resource set where <paramref name="resource"/> belongs to</param> /// <param name="parentResourceType">resource type whose properties needs to be populated</param> /// <returns>true if any properties were set; false otherwise.</returns> 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 <ResourceProperty> navProperties = new List <ResourceProperty>(); List <object> navPropertyValues = new List <object>(); #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); }