/// <summary> /// Reads an inner error payload. /// </summary> /// <param name="recursionDepth">The number of times this method has been called recursively.</param> /// <returns>An <see cref="ODataInnerError"/> representing the read inner error.</returns> /// <remarks> /// Pre-Condition: any - will throw if not StartObject /// Post-Condition: JsonNodeType.Property - The next property in the error value /// JsonNodeType.EndObject - The end of the error value /// </remarks> private ODataInnerError ReadInnerError(int recursionDepth) { Debug.Assert(this.JsonReader.DisableInStreamErrorDetection, "JsonReader.DisableInStreamErrorDetection"); this.JsonReader.AssertNotBuffering(); ValidationUtils.IncreaseAndValidateRecursionDepth(ref recursionDepth, this.MessageReaderSettings.MessageQuotas.MaxNestingDepth); this.JsonReader.ReadStartObject(); ODataInnerError innerError = new ODataInnerError(); ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertiesFoundBitField = ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.None; while (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.ReadPropertyName(); switch (propertyName) { case JsonConstants.ODataErrorInnerErrorMessageName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.MessageValue, JsonConstants.ODataErrorInnerErrorMessageName); innerError.Message = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorInnerErrorMessageName); break; case JsonConstants.ODataErrorInnerErrorTypeNameName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.TypeName, JsonConstants.ODataErrorInnerErrorTypeNameName); innerError.TypeName = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorInnerErrorTypeNameName); break; case JsonConstants.ODataErrorInnerErrorStackTraceName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.StackTrace, JsonConstants.ODataErrorInnerErrorStackTraceName); innerError.StackTrace = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorInnerErrorStackTraceName); break; case JsonConstants.ODataErrorInnerErrorInnerErrorName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.InnerError, JsonConstants.ODataErrorInnerErrorInnerErrorName); innerError.InnerError = this.ReadInnerError(recursionDepth); break; default: // skip any unsupported properties in the inner error this.JsonReader.SkipValue(); break; } } this.JsonReader.ReadEndObject(); this.JsonReader.AssertNotBuffering(); return(innerError); }
/// <summary> /// Reads the properties of an entity reference link. /// </summary> /// <param name="entityReferenceLinks">The <see cref="ODataEntityReferenceLinks"/> instance to set the read property values on.</param> /// <param name="propertiesFoundBitField">The bit field with all the properties already read.</param> /// <returns>true if the method found the 'results' property; otherwise false.</returns> private bool ReadEntityReferenceLinkProperties( ODataEntityReferenceLinks entityReferenceLinks, ref ODataVerboseJsonReaderUtils.EntityReferenceLinksWrapperPropertyBitMask propertiesFoundBitField) { Debug.Assert(entityReferenceLinks != null, "entityReferenceLinks != null"); this.JsonReader.AssertNotBuffering(); while (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.ReadPropertyName(); switch (propertyName) { case JsonConstants.ODataResultsName: ODataVerboseJsonReaderUtils.VerifyEntityReferenceLinksWrapperPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.EntityReferenceLinksWrapperPropertyBitMask.Results, JsonConstants.ODataResultsName); this.JsonReader.AssertNotBuffering(); return(true); case JsonConstants.ODataCountName: ODataVerboseJsonReaderUtils.VerifyEntityReferenceLinksWrapperPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.EntityReferenceLinksWrapperPropertyBitMask.Count, JsonConstants.ODataCountName); object countValue = this.JsonReader.ReadPrimitiveValue(); long? count = (long?)ODataVerboseJsonReaderUtils.ConvertValue( countValue, EdmCoreModel.Instance.GetInt64(true), this.MessageReaderSettings, this.Version, /*validateNullValue*/ true, propertyName); ODataVerboseJsonReaderUtils.ValidateCountPropertyInEntityReferenceLinks(count); entityReferenceLinks.Count = count; break; case JsonConstants.ODataNextLinkName: ODataVerboseJsonReaderUtils.VerifyEntityReferenceLinksWrapperPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.EntityReferenceLinksWrapperPropertyBitMask.NextPageLink, JsonConstants.ODataNextLinkName); string nextLinkString = this.JsonReader.ReadStringValue(JsonConstants.ODataNextLinkName); ODataVerboseJsonReaderUtils.ValidateEntityReferenceLinksStringProperty(nextLinkString, JsonConstants.ODataNextLinkName); entityReferenceLinks.NextPageLink = this.ProcessUriFromPayload(nextLinkString); break; default: // Skip all unrecognized properties this.JsonReader.SkipValue(); break; } } this.JsonReader.AssertNotBuffering(); return(false); }
/// <summary> /// Reads the type name from the value of a __metadata property. All other properties in the __metadata property value are ignored. /// </summary> /// <returns>The type name found, or null if none was found.</returns> /// <remarks> /// This method can be used in buffering and non-buffering mode. /// /// Pre-Condition: Fails if the current node is not a JsonNodeType.StartObject /// Post-Condition: JsonNodeType.Property - the next property after the __metadata property value. /// JsonNodeType.EndObject - if the __metadata property was the last property in the object. /// </remarks> internal string ReadTypeNameFromMetadataPropertyValue() { DebugUtils.CheckNoExternalCallers(); string typeName = null; // The value of the __metadata property must be an object if (this.JsonReader.NodeType != JsonNodeType.StartObject) { throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_MetadataPropertyMustHaveObjectValue(this.JsonReader.NodeType)); } this.JsonReader.ReadStartObject(); // Go over the properties and look for the type property. ODataVerboseJsonReaderUtils.MetadataPropertyBitMask propertiesFoundBitField = 0; while (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.ReadPropertyName(); if (string.CompareOrdinal(JsonConstants.ODataMetadataTypeName, propertyName) == 0) { ODataVerboseJsonReaderUtils.VerifyMetadataPropertyNotFound( ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.MetadataPropertyBitMask.Type, JsonConstants.ODataMetadataTypeName); object typeNameValue = this.JsonReader.ReadPrimitiveValue(); typeName = typeNameValue as string; if (typeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_InvalidTypeName(typeNameValue)); } } else { // Skip over the property value. this.JsonReader.SkipValue(); } } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndObject, "Only Property or EndObject can occur in Object scope."); this.JsonReader.ReadEndObject(); Debug.Assert( this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject, "Post-Condition: expected JsonNodeType.Property or JsonNodeType.EndObject"); return(typeName); }
/// <summary> /// Reads a primitive value. /// </summary> /// <param name="expectedValueTypeReference">The expected type reference of the value.</param> /// <param name="validateNullValue">true to validate null values; otherwise false.</param> /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param> /// <returns>The value of the primitive value.</returns> /// <remarks> /// Pre-Condition: none - Fails if the current node is not a JsonNodeType.PrimitiveValue /// Post-Condition: almost anything - the node after the primitive value. /// /// Made internal only for testability. /// </remarks> internal object ReadPrimitiveValue(IEdmPrimitiveTypeReference expectedValueTypeReference, bool validateNullValue, string propertyName) { DebugUtils.CheckNoExternalCallers(); this.JsonReader.AssertNotBuffering(); object result; if (expectedValueTypeReference != null && expectedValueTypeReference.IsSpatial()) { result = ODataJsonReaderCoreUtils.ReadSpatialValue( this.JsonReader, /*insideJsonObjectValue*/ false, this.VerboseJsonInputContext, expectedValueTypeReference, validateNullValue, this.recursionDepth, propertyName); } else { result = this.JsonReader.ReadPrimitiveValue(); if (expectedValueTypeReference != null && !this.MessageReaderSettings.DisablePrimitiveTypeConversion) { result = ODataVerboseJsonReaderUtils.ConvertValue( result, expectedValueTypeReference, this.MessageReaderSettings, this.Version, validateNullValue, propertyName); } } this.JsonReader.AssertNotBuffering(); return(result); }
/// <summary> /// Reads a primitive, complex or collection value. /// </summary> /// <param name="expectedTypeReference">The expected type reference of the property value.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use - if null the method should create a new one if necessary.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <param name="validateNullValue">true to validate null values; otherwise false.</param> /// <param name="propertyName">The name of the property whose value is being read, if applicable.</param> /// <returns>The value of the property read.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.PrimitiveValue - the value of the property is a primitive value /// JsonNodeType.StartObject - the value of the property is an object /// JsonNodeType.StartArray - the value of the property is an array - method will fail in this case. /// Post-Condition: almost anything - the node after the property value. /// /// Returns the value of the property read, which can be one of: /// - null /// - primitive value /// - <see cref="ODataComplexValue"/> /// - <see cref="ODataCollectionValue"/> /// </remarks> private object ReadNonEntityValueImplementation( IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, string propertyName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert( this.JsonReader.NodeType == JsonNodeType.PrimitiveValue || this.JsonReader.NodeType == JsonNodeType.StartObject || this.JsonReader.NodeType == JsonNodeType.StartArray, "Pre-Condition: expected JsonNodeType.PrimitiveValue or JsonNodeType.StartObject or JsonNodeType.StartArray"); Debug.Assert( expectedTypeReference == null || !expectedTypeReference.IsODataEntityTypeKind(), "Only primitive, complex or collection types can be read by this method."); Debug.Assert( expectedTypeReference == null || collectionValidator == null, "If an expected value type reference is specified, no collection validator must be provided."); this.JsonReader.AssertNotBuffering(); // Property values can be only primitives or objects. No property can have a JSON array value. JsonNodeType nodeType = this.JsonReader.NodeType; if (nodeType == JsonNodeType.StartArray) { throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_CannotReadPropertyValue(nodeType)); } // Try to read a null value object result; if (ODataJsonReaderCoreUtils.TryReadNullValue( this.JsonReader, this.VerboseJsonInputContext, expectedTypeReference, validateNullValue, propertyName)) { result = null; } else { // Read the payload type name string payloadTypeName = this.FindTypeNameInPayload(); SerializationTypeNameAnnotation serializationTypeNameAnnotation; EdmTypeKind targetTypeKind; IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*defaultPrimitivePayloadType*/ null, expectedTypeReference, payloadTypeName, this.Model, this.MessageReaderSettings, this.Version, this.GetNonEntityValueKind, out targetTypeKind, out serializationTypeNameAnnotation); switch (targetTypeKind) { case EdmTypeKind.Primitive: Debug.Assert(targetTypeReference == null || targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type."); IEdmPrimitiveTypeReference primitiveTargetTypeReference = targetTypeReference == null ? null : targetTypeReference.AsPrimitive(); if (payloadTypeName != null && !primitiveTargetTypeReference.IsSpatial()) { throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_InvalidPrimitiveTypeName(payloadTypeName)); } result = this.ReadPrimitiveValue( primitiveTargetTypeReference, validateNullValue, propertyName); break; case EdmTypeKind.Complex: Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type."); result = this.ReadComplexValueImplementation( targetTypeReference == null ? null : targetTypeReference.AsComplex(), payloadTypeName, serializationTypeNameAnnotation, duplicatePropertyNamesChecker); break; case EdmTypeKind.Collection: Debug.Assert(this.Version >= ODataVersion.V3, "Type resolution should already fail if we would decide to read a collection value in V1/V2 payload."); IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference); result = this.ReadCollectionValueImplementation( collectionTypeReference, payloadTypeName, serializationTypeNameAnnotation); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataVerboseJsonPropertyAndValueDeserializer_ReadPropertyValue)); } // If we have no expected type make sure the collection items are of the same kind and specify the same name. if (collectionValidator != null) { string payloadTypeNameFromResult = ODataVerboseJsonReaderUtils.GetPayloadTypeName(result); Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference."); collectionValidator.ValidateCollectionItem(payloadTypeNameFromResult, targetTypeKind); } } this.JsonReader.AssertNotBuffering(); return(result); }
/// <summary> /// Read a top-level error. /// </summary> /// <returns>An <see cref="ODataError"/> representing the read error.</returns> internal ODataError ReadTopLevelError() { DebugUtils.CheckNoExternalCallers(); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.None, "Pre-Condition: expected JsonNodeType.None, the reader must not have been used yet."); Debug.Assert(!this.JsonReader.DisableInStreamErrorDetection, "!JsonReader.DisableInStreamErrorDetection"); this.JsonReader.AssertNotBuffering(); // prevent the buffering JSON reader from detecting in-stream errors - we read the error ourselves // to throw proper exceptions this.JsonReader.DisableInStreamErrorDetection = true; ODataError error = new ODataError(); try { // Read the start of the payload (no "d" wrapper for top-level errors) this.ReadPayloadStart(/*isReadingNestedPayload*/ false, /*expectResponseWrapper*/ false); // Read the start of the error object this.JsonReader.ReadStartObject(); ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertiesFoundBitField = ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.None; while (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.ReadPropertyName(); if (string.CompareOrdinal(JsonConstants.ODataErrorName, propertyName) != 0) { // we only allow a single 'error' property for a top-level error object throw new ODataException(Strings.ODataJsonErrorDeserializer_TopLevelErrorWithInvalidProperty(propertyName)); } ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound(ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.Error, JsonConstants.ODataErrorName); this.JsonReader.ReadStartObject(); while (this.JsonReader.NodeType == JsonNodeType.Property) { propertyName = this.JsonReader.ReadPropertyName(); switch (propertyName) { case JsonConstants.ODataErrorCodeName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound(ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.Code, JsonConstants.ODataErrorCodeName); error.ErrorCode = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorCodeName); break; case JsonConstants.ODataErrorMessageName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound(ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.Message, JsonConstants.ODataErrorMessageName); this.JsonReader.ReadStartObject(); while (this.JsonReader.NodeType == JsonNodeType.Property) { propertyName = this.JsonReader.ReadPropertyName(); switch (propertyName) { case JsonConstants.ODataErrorMessageLanguageName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound(ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.MessageLanguage, JsonConstants.ODataErrorMessageLanguageName); error.MessageLanguage = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorMessageLanguageName); break; case JsonConstants.ODataErrorMessageValueName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound(ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.MessageValue, JsonConstants.ODataErrorMessageValueName); error.Message = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorMessageValueName); break; default: // we only allow a 'lang' and 'value' properties in the value of the 'message' property throw new ODataException(Strings.ODataJsonErrorDeserializer_TopLevelErrorMessageValueWithInvalidProperty(propertyName)); } } this.JsonReader.ReadEndObject(); break; case JsonConstants.ODataErrorInnerErrorName: ODataVerboseJsonReaderUtils.VerifyErrorPropertyNotFound(ref propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.InnerError, JsonConstants.ODataErrorInnerErrorName); error.InnerError = this.ReadInnerError(0 /* recursionDepth */); break; default: // we only allow a 'code', 'message' and 'innererror' properties in the value of the 'error' property throw new ODataException(Strings.ODataVerboseJsonErrorDeserializer_TopLevelErrorValueWithInvalidProperty(propertyName)); } } this.JsonReader.ReadEndObject(); } // Read the end of the error object this.JsonReader.ReadEndObject(); // Read the end of the response (no "d" wrapper for top-level errors) this.ReadPayloadEnd(/*isReadingNestedPayload*/ false, /*expectResponseWrapper*/ false); } finally { this.JsonReader.DisableInStreamErrorDetection = false; } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndOfInput, "Post-Condition: JsonNodeType.EndOfInput"); this.JsonReader.AssertNotBuffering(); return(error); }