/// <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> /// Checks whether the specified property has already been found before. /// </summary> /// <param name="propertiesFoundBitField"> /// The bit field which stores which properties of an error or inner error were found so far. /// </param> /// <param name="propertyFoundBitMask">The bit mask for the property to check.</param> /// <returns>true if the property has not been read before; otherwise false.</returns> internal static bool ErrorPropertyNotFound( ref ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertiesFoundBitField, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertyFoundBitMask) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(((int)propertyFoundBitMask & (((int)propertyFoundBitMask) - 1)) == 0, "propertyFoundBitMask is not a power of 2."); if ((propertiesFoundBitField & propertyFoundBitMask) == propertyFoundBitMask) { return(false); } propertiesFoundBitField |= propertyFoundBitMask; return(true); }
/// <summary> /// Try to read an inner error property value. /// </summary> /// <param name="innerError">An <see cref="ODataInnerError"/> instance that was read from the reader or null if none could be read.</param> /// <param name="recursionDepth">The number of times this method has been called recursively.</param> /// <returns>true if an <see cref="ODataInnerError"/> instance that was read; otherwise false.</returns> private bool TryReadInnerErrorPropertyValue(out ODataInnerError innerError, int recursionDepth) { Debug.Assert(this.currentBufferedNode.NodeType == JsonNodeType.Property, "this.currentBufferedNode.NodeType == JsonNodeType.Property"); Debug.Assert(this.parsingInStreamError, "this.parsingInStreamError"); this.AssertBuffering(); ValidationUtils.IncreaseAndValidateRecursionDepth(ref recursionDepth, this.maxInnerErrorDepth); // move the reader onto the property value this.ReadInternal(); // we expect a start-object node here if (this.currentBufferedNode.NodeType != JsonNodeType.StartObject) { innerError = null; return(false); } // read the start-object node this.ReadInternal(); innerError = new ODataInnerError(); // we expect one of the supported properties for the value (or end-object) ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertiesFoundBitmask = ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.None; while (this.currentBufferedNode.NodeType == JsonNodeType.Property) { // NOTE the Json reader already ensures that the value of a property node is a string string propertyName = (string)this.currentBufferedNode.Value; switch (propertyName) { case JsonConstants.ODataErrorInnerErrorMessageName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.MessageValue)) { return(false); } string message; if (this.TryReadErrorStringPropertyValue(out message)) { innerError.Message = message; } else { return(false); } break; case JsonConstants.ODataErrorInnerErrorTypeNameName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.TypeName)) { return(false); } string typeName; if (this.TryReadErrorStringPropertyValue(out typeName)) { innerError.TypeName = typeName; } else { return(false); } break; case JsonConstants.ODataErrorInnerErrorStackTraceName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.StackTrace)) { return(false); } string stackTrace; if (this.TryReadErrorStringPropertyValue(out stackTrace)) { innerError.StackTrace = stackTrace; } else { return(false); } break; case JsonConstants.ODataErrorInnerErrorInnerErrorName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.InnerError)) { return(false); } ODataInnerError nestedInnerError; if (this.TryReadInnerErrorPropertyValue(out nestedInnerError, recursionDepth)) { innerError.InnerError = nestedInnerError; } else { return(false); } break; default: // if we find a non-supported property in an inner error, we skip it this.SkipValueInternal(); break; } this.ReadInternal(); } Debug.Assert(this.currentBufferedNode.NodeType == JsonNodeType.EndObject, "this.currentBufferedNode.NodeType == JsonNodeType.EndObject"); return(true); }
/// <summary> /// Try to read the message property value of an error value. /// </summary> /// <param name="error">An <see cref="ODataError"/> instance to set the read message property values on.</param> /// <returns>true if the message property values could be read; otherwise false.</returns> private bool TryReadMessagePropertyValue(ODataError error) { Debug.Assert(error != null, "error != null"); Debug.Assert(this.currentBufferedNode.NodeType == JsonNodeType.Property, "this.currentBufferedNode.NodeType == JsonNodeType.Property"); Debug.Assert(this.parsingInStreamError, "this.parsingInStreamError"); this.AssertBuffering(); // move the reader onto the property value this.ReadInternal(); // we expect a start-object node here if (this.currentBufferedNode.NodeType != JsonNodeType.StartObject) { return(false); } // read the start-object node this.ReadInternal(); // we expect one of the supported properties for the value (or end-object) ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertiesFoundBitmask = ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.None; while (this.currentBufferedNode.NodeType == JsonNodeType.Property) { // NOTE the Json reader already ensures that the value of a property node is a string string propertyName = (string)this.currentBufferedNode.Value; switch (propertyName) { case JsonConstants.ODataErrorMessageLanguageName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.MessageLanguage)) { return(false); } string lang; if (this.TryReadErrorStringPropertyValue(out lang)) { error.MessageLanguage = lang; } else { return(false); } break; case JsonConstants.ODataErrorMessageValueName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.MessageValue)) { return(false); } string message; if (this.TryReadErrorStringPropertyValue(out message)) { error.Message = message; } else { return(false); } break; default: // if we find a non-supported property we don't treat this as an error return(false); } this.ReadInternal(); } Debug.Assert(this.currentBufferedNode.NodeType == JsonNodeType.EndObject, "this.currentBufferedNode.NodeType == JsonNodeType.EndObject"); return(true); }
/// <summary> /// Try to read an error structure from the stream. Return null if no error structure can be read. /// </summary> /// <param name="error">An <see cref="ODataError"/> instance that was read from the reader or null if none could be read.</param> /// <returns>true if an <see cref="ODataError"/> instance that was read; otherwise false.</returns> private bool TryReadInStreamErrorPropertyValue(out ODataError error) { Debug.Assert(this.parsingInStreamError, "this.parsingInStreamError"); this.AssertBuffering(); error = null; // we expect a start-object node here if (this.currentBufferedNode.NodeType != JsonNodeType.StartObject) { return(false); } // read the start-object node this.ReadInternal(); error = new ODataError(); // we expect one of the supported properties for the value (or end-object) ODataVerboseJsonReaderUtils.ErrorPropertyBitMask propertiesFoundBitmask = ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.None; while (this.currentBufferedNode.NodeType == JsonNodeType.Property) { // NOTE the Json reader already ensures that the value of a property node is a string string propertyName = (string)this.currentBufferedNode.Value; switch (propertyName) { case JsonConstants.ODataErrorCodeName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.Code)) { return(false); } string errorCode; if (this.TryReadErrorStringPropertyValue(out errorCode)) { error.ErrorCode = errorCode; } else { return(false); } break; case JsonConstants.ODataErrorMessageName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.Message)) { return(false); } if (!this.TryReadMessagePropertyValue(error)) { return(false); } break; case JsonConstants.ODataErrorInnerErrorName: if (!ODataVerboseJsonReaderUtils.ErrorPropertyNotFound(ref propertiesFoundBitmask, ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.InnerError)) { return(false); } ODataInnerError innerError; if (!this.TryReadInnerErrorPropertyValue(out innerError, 0 /*recursionDepth */)) { return(false); } error.InnerError = innerError; break; default: // if we find a non-supported property we don't treat this as an error return(false); } this.ReadInternal(); } // read the end object Debug.Assert(this.currentBufferedNode.NodeType == JsonNodeType.EndObject, "this.currentBufferedNode.NodeType == JsonNodeType.EndObject"); this.ReadInternal(); // if we don't find any properties it is not a valid error object return(propertiesFoundBitmask != ODataVerboseJsonReaderUtils.ErrorPropertyBitMask.None); }
/// <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); }