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
- Policy.cs
- XhtmlBasicTextViewAdapter.cs
- SelectionEditor.cs
- BoundField.cs
- RawStylusInputReport.cs
- ResourceDescriptionAttribute.cs
- MessageTraceRecord.cs
- MessageSecurityOverMsmqElement.cs
- SafeViewOfFileHandle.cs
- OutputScopeManager.cs
- x509utils.cs
- MimeImporter.cs
- XmlDataSource.cs
- StickyNote.cs
- WindowsFormsHostPropertyMap.cs
- ProgressBarBrushConverter.cs
- ListViewCancelEventArgs.cs
- HandlerWithFactory.cs
- CLRBindingWorker.cs
- AsyncDataRequest.cs
- DelegateTypeInfo.cs
- SessionSwitchEventArgs.cs
- DiscoveryDocumentSearchPattern.cs
- SQLDateTimeStorage.cs
- BookmarkTable.cs
- SmtpNtlmAuthenticationModule.cs
- SettingsPropertyIsReadOnlyException.cs
- MultipleViewProviderWrapper.cs
- CompositeScriptReferenceEventArgs.cs
- XMLUtil.cs
- EditCommandColumn.cs
- wmiutil.cs
- TypedReference.cs
- RemotingException.cs
- HMACSHA256.cs
- DataContext.cs
- EmbeddedObject.cs
- Int64Storage.cs
- PlaceHolder.cs
- TextRunCacheImp.cs
- FtpCachePolicyElement.cs
- DataGridTextBox.cs
- PropertyPathWorker.cs
- Nullable.cs
- SrgsSubset.cs
- TemplateContentLoader.cs
- DataRowView.cs
- userdatakeys.cs
- OpacityConverter.cs
- ModuleBuilderData.cs
- SecurityState.cs
- ScrollEvent.cs
- ConfigXmlSignificantWhitespace.cs
- DataGridHeaderBorder.cs
- HtmlWindow.cs
- WindowsTitleBar.cs
- Compiler.cs
- HtmlTextArea.cs
- dsa.cs
- SiteMapPath.cs
- TextRangeBase.cs
- VisualStates.cs
- StatusBarDrawItemEvent.cs
- EntityContainerRelationshipSet.cs
- SR.cs
- XmlCDATASection.cs
- ObjectDataSourceFilteringEventArgs.cs
- SymmetricAlgorithm.cs
- WmlFormAdapter.cs
- HttpModuleActionCollection.cs
- SupportedAddressingMode.cs
- FloatUtil.cs
- SendMailErrorEventArgs.cs
- GetWinFXPath.cs
- WebPartCloseVerb.cs
- Pair.cs
- WasEndpointConfigContainer.cs
- SerTrace.cs
- CodeSubDirectoriesCollection.cs
- SecurityIdentifierElement.cs
- InternalsVisibleToAttribute.cs
- TextClipboardData.cs
- SerializableTypeCodeDomSerializer.cs
- XmlNodeChangedEventArgs.cs
- EntitySetRetriever.cs
- ProtocolsConfiguration.cs
- XhtmlConformanceSection.cs
- FilteredAttributeCollection.cs
- FormViewCommandEventArgs.cs
- XhtmlStyleClass.cs
- RegexTree.cs
- QueryServiceConfigHandle.cs
- TraceUtility.cs
- dataobject.cs
- PageBreakRecord.cs
- MiniLockedBorderGlyph.cs
- BitmapFrame.cs
- SqlEnums.cs
- UriTemplateTableMatchCandidate.cs
- SortQuery.cs