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
![Network programming in C#, Network Programming in VB.NET, Network Programming in .NET](/images/book.jpg)
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- URIFormatException.cs
- DbExpressionVisitor.cs
- MemberRestriction.cs
- DoubleCollection.cs
- SafeLibraryHandle.cs
- ExtendedProperty.cs
- ChannelManager.cs
- LinqDataSourceDeleteEventArgs.cs
- FixedSOMElement.cs
- SourceChangedEventArgs.cs
- TopClause.cs
- Viewport3DVisual.cs
- behaviorssection.cs
- RuleInfoComparer.cs
- WsatTransactionInfo.cs
- TimeSpanMinutesConverter.cs
- ReaderWriterLock.cs
- PhysicalAddress.cs
- ModelPerspective.cs
- MetaTableHelper.cs
- LoadGrammarCompletedEventArgs.cs
- BinaryObjectReader.cs
- HierarchicalDataTemplate.cs
- XsltContext.cs
- OleServicesContext.cs
- NavigationWindow.cs
- QueryCursorEventArgs.cs
- PersonalizationAdministration.cs
- Grid.cs
- RawStylusInputCustomDataList.cs
- DispatchChannelSink.cs
- RSAPKCS1KeyExchangeFormatter.cs
- RenameRuleObjectDialog.cs
- SubpageParagraph.cs
- BinaryFormatterWriter.cs
- SocketPermission.cs
- SchemaImporterExtensionElement.cs
- MobileErrorInfo.cs
- SecurityPolicySection.cs
- MemberDescriptor.cs
- RoutedEventValueSerializer.cs
- BulletDecorator.cs
- LocalValueEnumerator.cs
- CachedPathData.cs
- AsyncResult.cs
- Literal.cs
- NotifyInputEventArgs.cs
- TransformerInfoCollection.cs
- MimeWriter.cs
- Binding.cs
- ProxyAttribute.cs
- DataService.cs
- DeadCharTextComposition.cs
- cookiecontainer.cs
- EasingKeyFrames.cs
- LogicalTreeHelper.cs
- RegexMatchCollection.cs
- PropertyCollection.cs
- DataListItem.cs
- XsdBuilder.cs
- HeaderedContentControl.cs
- TextPenaltyModule.cs
- MemoryPressure.cs
- WorkflowServiceHostFactory.cs
- SliderAutomationPeer.cs
- Parser.cs
- WindowPattern.cs
- DrawingAttributeSerializer.cs
- BaseCollection.cs
- InsufficientMemoryException.cs
- HMACSHA384.cs
- SQLDecimal.cs
- PropertyMetadata.cs
- ComplexBindingPropertiesAttribute.cs
- _LocalDataStoreMgr.cs
- DBDataPermissionAttribute.cs
- EventSourceCreationData.cs
- OleDbDataAdapter.cs
- Shape.cs
- XmlSchemaType.cs
- UIElementIsland.cs
- RootBrowserWindow.cs
- WebBaseEventKeyComparer.cs
- AspNetHostingPermission.cs
- RbTree.cs
- DataControlExtensions.cs
- HttpWebRequest.cs
- HttpPostLocalhostServerProtocol.cs
- FrameworkReadOnlyPropertyMetadata.cs
- RouteParametersHelper.cs
- PerfCounterSection.cs
- TransformCollection.cs
- MultiAsyncResult.cs
- GridToolTip.cs
- DataTableClearEvent.cs
- TextHidden.cs
- PersistenceIOParticipant.cs
- ElementAction.cs
- VectorAnimationUsingKeyFrames.cs
- DataTableMapping.cs