/// <summary>Initializes a new <see cref="JsonSerializer"/> for the specified stream.</summary> /// <param name="requestStream">Input stream from which JSON content must be read.</param> /// <param name="encoding">Encoding to use for the stream.</param> /// <param name="update">indicates whether this is a update operation or not</param> /// <param name="dataService">Data service for which the deserializer will act.</param> /// <param name="tracker">Tracker to use for modifications.</param> 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)); }
/// <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; }
/// <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; }