Code:
/ 4.0 / 4.0 / untmp / 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 /// 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)); } ///for the specified stream. returns the content format for the deserializer protected override ContentFormat ContentFormat { get { return ContentFormat.Json; } } ////// Converts the given value to the expected type as per json reader rules /// Make sure these rules are in [....] with jsonwriter. /// /// value to the converted /// name of the property whose value is getting converted /// clr type to which the value needs to be converted to /// 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(Dictionarymetadata) { 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 wherebelongs 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; ListnavProperties = new List (); List
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- AsymmetricAlgorithm.cs
- WebProxyScriptElement.cs
- System.Data_BID.cs
- WsdlInspector.cs
- _CookieModule.cs
- SystemDropShadowChrome.cs
- SafeCertificateStore.cs
- TextOptionsInternal.cs
- XmlnsDictionary.cs
- DbDataSourceEnumerator.cs
- XmlSchemaChoice.cs
- BitmapCodecInfo.cs
- TableItemStyle.cs
- AnnouncementSendsAsyncResult.cs
- FileFormatException.cs
- TableRowsCollectionEditor.cs
- XmlIlTypeHelper.cs
- Triplet.cs
- RandomNumberGenerator.cs
- SingleResultAttribute.cs
- BinaryParser.cs
- SynchronizationLockException.cs
- MdImport.cs
- ButtonColumn.cs
- HtmlInputFile.cs
- CodeAttributeDeclarationCollection.cs
- XmlCustomFormatter.cs
- Assembly.cs
- TextEndOfParagraph.cs
- XmlEncoding.cs
- Ray3DHitTestResult.cs
- MultidimensionalArrayItemReference.cs
- Int16Converter.cs
- relpropertyhelper.cs
- NonClientArea.cs
- DataGridViewBand.cs
- Subset.cs
- CancelRequestedQuery.cs
- InputScopeNameConverter.cs
- WorkflowMessageEventHandler.cs
- QueryPageSettingsEventArgs.cs
- RadioButtonStandardAdapter.cs
- SerialReceived.cs
- Attributes.cs
- XmlAtomicValue.cs
- SpeakCompletedEventArgs.cs
- SR.cs
- XamlStackWriter.cs
- Win32.cs
- EnumerableRowCollection.cs
- SynchronizedInputHelper.cs
- AvtEvent.cs
- MessageEnumerator.cs
- WindowsSpinner.cs
- DragDropHelper.cs
- ListViewHitTestInfo.cs
- SessionSwitchEventArgs.cs
- XmlHierarchicalDataSourceView.cs
- ListBindingConverter.cs
- JsonQNameDataContract.cs
- KnownColorTable.cs
- QueryCursorEventArgs.cs
- ImageField.cs
- Parameter.cs
- StringHandle.cs
- StandardToolWindows.cs
- CommandID.cs
- RectangleGeometry.cs
- arclist.cs
- DocumentEventArgs.cs
- RequestCacheManager.cs
- MobileUITypeEditor.cs
- MetafileHeaderWmf.cs
- ContainerUtilities.cs
- SqlDataSourceView.cs
- OptimisticConcurrencyException.cs
- MenuItemStyle.cs
- AutomationPeer.cs
- MetadataConversionError.cs
- WorkflowDefinitionContext.cs
- PeerCustomResolverElement.cs
- SmtpFailedRecipientException.cs
- DataBoundControl.cs
- SetterBase.cs
- RuntimeWrappedException.cs
- EncryptedType.cs
- EntityKeyElement.cs
- DesignerForm.cs
- TypeSemantics.cs
- Composition.cs
- DomainUpDown.cs
- DynamicUpdateCommand.cs
- StylusPointProperty.cs
- ListViewGroup.cs
- FontFamilyConverter.cs
- oledbmetadatacollectionnames.cs
- OAVariantLib.cs
- ContextQuery.cs
- EntityContainerEmitter.cs
- EditBehavior.cs