예제 #1
0
        /// <summary>
        /// Reads and validates a string value from the json reader.
        /// </summary>
        /// <param name="annotationName">The name of the annotation being read (used for error reporting).</param>
        /// <returns>The string that was read.</returns>
        internal string ReadAndValidateAnnotationStringValue(string annotationName)
        {
            string valueRead = this.JsonReader.ReadStringValue(annotationName);

            ODataJsonLightReaderUtils.ValidateAnnotationValue(valueRead, annotationName);
            return(valueRead);
        }
예제 #2
0
        /// <summary>
        /// Reads a property value which occurs in the "error" object scope.
        /// </summary>
        /// <param name="error">The <see cref="ODataError"/> object to update with the data from this property value.</param>
        /// <param name="propertyName">The name of the property whose value is to be read.</param>
        /// <param name="duplicationPropertyNameChecker">PropertyAndAnnotationCollector to use for extracting property annotations
        /// targetting any custom instance annotations on the error.</param>
        /// <remarks>
        /// Pre-Condition:  any                         - The value of the property being read.
        /// Post-Condition: JsonNodeType.Property       - The property after the one being read.
        ///                 JsonNodeType.EndObject      - The end of the "error" object.
        ///                 any                         - Anything else after the property value is an invalid payload (but won't fail in this method).
        /// </remarks>
        private void ReadPropertyValueInODataErrorObject(ODataError error, string propertyName, PropertyAndAnnotationCollector duplicationPropertyNameChecker)
        {
            switch (propertyName)
            {
            case JsonConstants.ODataErrorCodeName:
                error.ErrorCode = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorCodeName);
                break;

            case JsonConstants.ODataErrorMessageName:
                error.Message = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorMessageName);
                break;

            case JsonConstants.ODataErrorTargetName:
                error.Target = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorTargetName);
                break;

            case JsonConstants.ODataErrorDetailsName:
                error.Details = this.ReadDetails();
                break;

            case JsonConstants.ODataErrorInnerErrorName:
                error.InnerError = this.ReadInnerError(0 /* recursionDepth */);
                break;

            default:
                // See if it's an instance annotation
                if (ODataJsonLightReaderUtils.IsAnnotationProperty(propertyName))
                {
                    ODataJsonLightPropertyAndValueDeserializer valueDeserializer = new ODataJsonLightPropertyAndValueDeserializer(this.JsonLightInputContext);
                    object typeName = null;

                    duplicationPropertyNameChecker.GetODataPropertyAnnotations(propertyName)
                    .TryGetValue(ODataAnnotationNames.ODataType, out typeName);

                    var value = valueDeserializer.ReadNonEntityValue(
                        typeName as string,
                        null /*expectedValueTypeReference*/,
                        null /*duplicatePropertiesNamesChecker*/,
                        null /*collectionValidator*/,
                        false /*validateNullValue*/,
                        false /*isTopLevelPropertyValue*/,
                        false /*insideResourceValue*/,
                        propertyName);

                    error.GetInstanceAnnotations().Add(new ODataInstanceAnnotation(propertyName, value.ToODataValue()));
                }
                else
                {
                    // we only allow a 'code', 'message', 'target', 'details, and 'innererror' properties
                    // in the value of the 'error' property or custom instance annotations
                    throw new ODataException(Strings.ODataJsonLightErrorDeserializer_TopLevelErrorValueWithInvalidProperty(propertyName));
                }

                break;
            }
        }
예제 #3
0
        /// <summary>
        /// Process the current property annotation.
        /// </summary>
        /// <param name="annotatedPropertyName">The name being annotated. Can be a property or an instance annotation.</param>
        /// <param name="annotationName">The annotation targeting the <paramref name="annotatedPropertyName"/>.</param>
        /// <param name="propertyAndAnnotationCollector">The duplicate property names checker.</param>
        /// <param name="readPropertyAnnotationValue">Callback to read the property annotation value.</param>
        private void ProcessPropertyAnnotation(string annotatedPropertyName, string annotationName, PropertyAndAnnotationCollector propertyAndAnnotationCollector, Func <string, object> readPropertyAnnotationValue)
        {
            // We don't currently support annotation targeting an instance annotation except for the @odata.type property annotation.
            if (ODataJsonLightReaderUtils.IsAnnotationProperty(annotatedPropertyName) && string.CompareOrdinal(annotationName, ODataAnnotationNames.ODataType) != 0)
            {
                throw new ODataException(Strings.ODataJsonLightDeserializer_OnlyODataTypeAnnotationCanTargetInstanceAnnotation(annotationName, annotatedPropertyName, ODataAnnotationNames.ODataType));
            }

            ReadODataOrCustomInstanceAnnotationValue(annotatedPropertyName, annotationName, propertyAndAnnotationCollector, readPropertyAnnotationValue);
        }
예제 #4
0
        /// <summary>
        /// Reads and validates a value from the json reader and processes it as a long.
        /// The input value could be string or number
        /// </summary>
        /// <param name="annotationName">The name of the annotation being read.</param>
        /// <returns>The long that is read.</returns>
        internal long ReadAndValidateAnnotationAsLongForIeee754Compatible(string annotationName)
        {
            object value = this.JsonReader.ReadPrimitiveValue();

            ODataJsonLightReaderUtils.ValidateAnnotationValue(value, annotationName);

            if ((value is string) ^ this.JsonReader.IsIeee754Compatible)
            {
                throw new ODataException(Strings.ODataJsonReaderUtils_ConflictBetweenInputFormatAndParameter(Metadata.EdmConstants.EdmInt64TypeName));
            }

            return((long)ODataJsonLightReaderUtils.ConvertValue(
                       value,
                       EdmCoreModel.Instance.GetInt64(false),
                       this.MessageReaderSettings,
                       /*validateNullValue*/ true,
                       annotationName,
                       this.JsonLightInputContext.PayloadValueConverter));
        }
예제 #5
0
 /// <summary>
 /// Reads built-in "odata." or custom instance annotation's value.
 /// </summary>
 /// <param name="annotatedPropertyName">The property name.</param>
 /// <param name="annotationName">The annotation name</param>
 /// <param name="propertyAndAnnotationCollector">The PropertyAndAnnotationCollector.</param>
 /// <param name="readPropertyAnnotationValue">Callback to read the property annotation value.</param>
 private void ReadODataOrCustomInstanceAnnotationValue(string annotatedPropertyName, string annotationName, PropertyAndAnnotationCollector propertyAndAnnotationCollector, Func <string, object> readPropertyAnnotationValue)
 {
     // Read over the property name.
     this.JsonReader.Read();
     if (ODataJsonLightReaderUtils.IsODataAnnotationName(annotationName))
     {
         // OData annotations are read
         propertyAndAnnotationCollector.AddODataPropertyAnnotation(annotatedPropertyName, annotationName, readPropertyAnnotationValue(annotationName));
     }
     else
     {
         if (this.ShouldSkipCustomInstanceAnnotation(annotationName) || (this is ODataJsonLightErrorDeserializer && this.MessageReaderSettings.ShouldIncludeAnnotation == null))
         {
             propertyAndAnnotationCollector.CheckIfPropertyOpenForAnnotations(annotatedPropertyName, annotationName);
             this.JsonReader.SkipValue();
         }
         else
         {
             Debug.Assert(ReadPropertyCustomAnnotationValue != null, "readPropertyCustomAnnotationValue != null");
             propertyAndAnnotationCollector.AddCustomPropertyAnnotation(annotatedPropertyName, annotationName, ReadPropertyCustomAnnotationValue(propertyAndAnnotationCollector, annotationName));
         }
     }
 }
예제 #6
0
        /// <summary>
        /// Parses JSON object property starting with the current position of the JSON reader.
        /// </summary>
        /// <param name="propertyAndAnnotationCollector">The duplicate property names checker to use, it will also store the property annotations found.</param>
        /// <param name="readPropertyAnnotationValue">Function called to read property annotation value.</param>
        /// <param name="parsedPropertyName">The name of the property or instance annotation found.</param>
        /// <returns>
        /// PropertyWithValue - a property with value was found. The <paramref name="parsedPropertyName"/> contains the name of the property.
        ///                     The reader is positioned on the property value.
        /// PropertyWithoutValue - a property without a value was found. The <paramref name="parsedPropertyName"/> contains the name of the property.
        ///                        The reader is positioned on the node after property annotations (so either a property or end of object).
        /// ODataInstanceAnnotation - an odata instance annotation was found. The <paramref name="parsedPropertyName"/> contains the name of the annotation.
        ///                      The reader is positioned on the value of the annotation.
        /// CustomInstanceAnnotation - a custom instance annotation was found. The <paramref name="parsedPropertyName"/> contains the name of the annotation.
        ///                      The reader is positioned on the value of the annotation.
        /// MetadataReferenceProperty - a property which is a reference into the metadata was found.
        ///                             The reader is positioned on the value of the property.
        /// EndOfObject - end of the object scope was reached and no properties are to be reported. The <paramref name="parsedPropertyName"/> is null.
        ///               This can only happen if there's a property annotation which is ignored (for example custom one) at the end of the object.
        /// </returns>
        private PropertyParsingResult ParseProperty(
            PropertyAndAnnotationCollector propertyAndAnnotationCollector,
            Func <string, object> readPropertyAnnotationValue,
            out string parsedPropertyName)
        {
            Debug.Assert(propertyAndAnnotationCollector != null, "propertyAndAnnotationCollector != null");
            Debug.Assert(readPropertyAnnotationValue != null, "readPropertyAnnotationValue != null");
            string lastPropertyAnnotationNameFound = null;

            parsedPropertyName = null;

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                string nameFromReader = this.JsonReader.GetPropertyName();

                string propertyNameFromReader, annotationNameFromReader;
                bool   isPropertyAnnotation = TryParsePropertyAnnotation(nameFromReader, out propertyNameFromReader, out annotationNameFromReader);

                // reading a nested delta resource set
                if (isPropertyAnnotation && String.CompareOrdinal(this.CompleteSimplifiedODataAnnotation(annotationNameFromReader), ODataAnnotationNames.ODataDelta) == 0)
                {
                    // Read over the property name.
                    this.JsonReader.Read();
                    parsedPropertyName = propertyNameFromReader;
                    return(PropertyParsingResult.NestedDeltaResourceSet);
                }

                bool isInstanceAnnotation = false;
                if (!isPropertyAnnotation)
                {
                    isInstanceAnnotation   = IsInstanceAnnotation(nameFromReader);
                    propertyNameFromReader = isInstanceAnnotation ? this.CompleteSimplifiedODataAnnotation(nameFromReader.Substring(1)) : nameFromReader;
                }

                // If parsedPropertyName is set and is different from the property name the reader is currently on,
                // we have parsed a property annotation for a different property than the one at the current position.
                if (parsedPropertyName != null && string.CompareOrdinal(parsedPropertyName, propertyNameFromReader) != 0)
                {
                    if (ODataJsonLightReaderUtils.IsAnnotationProperty(parsedPropertyName))
                    {
                        throw new ODataException(Strings.ODataJsonLightDeserializer_AnnotationTargetingInstanceAnnotationWithoutValue(lastPropertyAnnotationNameFound, parsedPropertyName));
                    }

                    return(PropertyParsingResult.PropertyWithoutValue);
                }

                object annotationValue = null;
                if (isPropertyAnnotation)
                {
                    annotationNameFromReader = this.CompleteSimplifiedODataAnnotation(annotationNameFromReader);

                    // If this is a unknown odata annotation targeting a property, we skip over it. See remark on the method SkippedOverUnknownODataAnnotation() for detailed explanation.
                    // Note that we don't skip over unknown odata annotations targeting another annotation. We don't allow annotations (except odata.type) targeting other annotations,
                    // so this.ProcessPropertyAnnotation() will test and fail for that case.
                    if (!ODataJsonLightReaderUtils.IsAnnotationProperty(propertyNameFromReader) && this.SkippedOverUnknownODataAnnotation(annotationNameFromReader, out annotationValue))
                    {
                        propertyAndAnnotationCollector.AddODataPropertyAnnotation(propertyNameFromReader, annotationNameFromReader, annotationValue);
                        continue;
                    }

                    // We have another property annotation for the same property we parsed.
                    parsedPropertyName = propertyNameFromReader;
                    lastPropertyAnnotationNameFound = annotationNameFromReader;

                    this.ProcessPropertyAnnotation(propertyNameFromReader, annotationNameFromReader, propertyAndAnnotationCollector, readPropertyAnnotationValue);
                    continue;
                }

                // If this is a unknown odata annotation, skip over it. See remark on the method SkippedOverUnknownODataAnnotation() for detailed explanation.
                if (isInstanceAnnotation && this.SkippedOverUnknownODataAnnotation(propertyNameFromReader, out annotationValue))
                {
                    // collect 'odata.<unknown>' annotation:
                    // here we know the original property name contains no '@', but '.' dot
                    Debug.Assert(annotationNameFromReader == null, "annotationNameFromReader == null");
                    propertyAndAnnotationCollector.AddODataScopeAnnotation(propertyNameFromReader, annotationValue);
                    continue;
                }

                // We are encountering the property name for the first time.
                // Don't read over property name, as that would cause the buffering reader to read ahead in the StartObject
                // state, which would break our ability to stream inline json. Instead, callers of ParseProperty will have to
                // call this.JsonReader.Read() as appropriate to read past the property name.
                parsedPropertyName = propertyNameFromReader;

                if (!isInstanceAnnotation && ODataJsonLightUtils.IsMetadataReferenceProperty(propertyNameFromReader))
                {
                    return(PropertyParsingResult.MetadataReferenceProperty);
                }

                if (!isInstanceAnnotation && !ODataJsonLightReaderUtils.IsAnnotationProperty(propertyNameFromReader))
                {
                    // Normal property
                    return(PropertyParsingResult.PropertyWithValue);
                }

                // collect 'xxx.yyyy' annotation:
                // here we know the original property name contains no '@', but '.' dot
                Debug.Assert(annotationNameFromReader == null, "annotationNameFromReader == null");

                // Handle 'odata.XXXXX' annotations
                if (isInstanceAnnotation && ODataJsonLightReaderUtils.IsODataAnnotationName(propertyNameFromReader))
                {
                    return(PropertyParsingResult.ODataInstanceAnnotation);
                }

                // Handle custom annotations
                return(PropertyParsingResult.CustomInstanceAnnotation);
            }

            this.AssertJsonCondition(JsonNodeType.EndObject);
            if (parsedPropertyName != null)
            {
                if (ODataJsonLightReaderUtils.IsAnnotationProperty(parsedPropertyName))
                {
                    throw new ODataException(Strings.ODataJsonLightDeserializer_AnnotationTargetingInstanceAnnotationWithoutValue(lastPropertyAnnotationNameFound, parsedPropertyName));
                }

                return(PropertyParsingResult.PropertyWithoutValue);
            }

            return(PropertyParsingResult.EndOfObject);
        }
        /// <summary>
        /// Detects the payload kind(s).
        /// </summary>
        /// <param name="detectionInfo">Additional information available for the payload kind detection.</param>
        /// <returns>An enumerable of zero, one or more payload kinds that were detected from looking at the payload in the message stream.</returns>
        private IEnumerable <ODataPayloadKind> DetectPayloadKindImplementation(ODataPayloadKindDetectionInfo detectionInfo)
        {
            Debug.Assert(detectionInfo != null, "detectionInfo != null");
            Debug.Assert(this.JsonReader.DisableInStreamErrorDetection, "The in-stream error detection should be disabled for payload kind detection.");

            this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject);

            // If we found a context URI and parsed it, look at the detected payload kind and return it.
            if (this.ContextUriParseResult != null)
            {
                return(this.ContextUriParseResult.DetectedPayloadKinds);
            }

            // Otherwise this is a payload without context URI and we have to start sniffing; only error payloads
            // don't have a context URI so check for a single 'error' property (ignoring custom annotations).
            ODataError error = null;

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                string propertyName = this.JsonReader.ReadPropertyName();
                string annotatedPropertyName, annotationName;
                if (!ODataJsonLightDeserializer.TryParsePropertyAnnotation(propertyName, out annotatedPropertyName, out annotationName))
                {
                    if (ODataJsonLightReaderUtils.IsAnnotationProperty(propertyName))
                    {
                        if (propertyName != null && propertyName.StartsWith(JsonLightConstants.ODataPropertyAnnotationSeparatorChar + JsonLightConstants.ODataAnnotationNamespacePrefix, System.StringComparison.Ordinal))
                        {
                            // Any @odata.* instance annotations are not allowed for errors.
                            return(Enumerable.Empty <ODataPayloadKind>());
                        }
                        else
                        {
                            // Skip custom instance annotations
                            this.JsonReader.SkipValue();
                        }
                    }
                    else
                    {
                        if (string.CompareOrdinal(JsonLightConstants.ODataErrorPropertyName, propertyName) == 0)
                        {
                            // If we find multiple errors or an invalid error value, this is not an error payload.
                            if (error != null || !this.JsonReader.StartBufferingAndTryToReadInStreamErrorPropertyValue(out error))
                            {
                                return(Enumerable.Empty <ODataPayloadKind>());
                            }

                            // At this point we successfully read the first error property.
                            // Skip the error value and check whether there are more properties.
                            this.JsonReader.SkipValue();
                        }
                        else
                        {
                            // if it contains non-annotation property, it is not an error payload.
                            return(Enumerable.Empty <ODataPayloadKind>());
                        }
                    }
                }
                else
                {
                    // Property annotation
                    return(Enumerable.Empty <ODataPayloadKind>());
                }
            }

            // If we got here without finding a context URI or an error payload, we don't know what this is.
            if (error == null)
            {
                return(Enumerable.Empty <ODataPayloadKind>());
            }

            return(new ODataPayloadKind[] { ODataPayloadKind.Error });
        }
        /// <summary>
        /// Asynchronously reads a property value which occurs in the "error" object scope.
        /// </summary>
        /// <param name="error">The <see cref="ODataError"/> object to update with the data from this property value.</param>
        /// <param name="propertyName">The name of the property whose value is to be read.</param>
        /// <param name="duplicationPropertyNameChecker"><see cref="PropertyAndAnnotationCollector"/> to use for extracting property annotations
        /// targeting any custom instance annotations on the error.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        /// <remarks>
        /// Pre-Condition:  any                         - The value of the property being read.
        /// Post-Condition: JsonNodeType.Property       - The property after the one being read.
        ///                 JsonNodeType.EndObject      - The end of the "error" object.
        ///                 any                         - Anything else after the property value is an invalid payload (but won't fail in this method).
        /// </remarks>
        private async Task ReadPropertyValueInODataErrorObjectAsync(ODataError error, string propertyName, PropertyAndAnnotationCollector duplicationPropertyNameChecker)
        {
            switch (propertyName)
            {
            case JsonConstants.ODataErrorCodeName:
                error.ErrorCode = await this.JsonReader.ReadStringValueAsync(JsonConstants.ODataErrorCodeName)
                                  .ConfigureAwait(false);

                break;

            case JsonConstants.ODataErrorMessageName:
                error.Message = await this.JsonReader.ReadStringValueAsync(JsonConstants.ODataErrorMessageName)
                                .ConfigureAwait(false);

                break;

            case JsonConstants.ODataErrorTargetName:
                error.Target = await this.JsonReader.ReadStringValueAsync(JsonConstants.ODataErrorTargetName)
                               .ConfigureAwait(false);

                break;

            case JsonConstants.ODataErrorDetailsName:
                error.Details = await this.ReadErrorDetailsAsync()
                                .ConfigureAwait(false);

                break;

            case JsonConstants.ODataErrorInnerErrorName:
                error.InnerError = await this.ReadInnerErrorAsync(recursionDepth : 0)
                                   .ConfigureAwait(false);

                break;

            default:
                // See if it's an instance annotation
                if (ODataJsonLightReaderUtils.IsAnnotationProperty(propertyName))
                {
                    ODataJsonLightPropertyAndValueDeserializer propertyAndValueDeserializer = new ODataJsonLightPropertyAndValueDeserializer(this.JsonLightInputContext);
                    object typeName;

                    duplicationPropertyNameChecker.GetODataPropertyAnnotations(propertyName).TryGetValue(ODataAnnotationNames.ODataType, out typeName);

                    var value = await propertyAndValueDeserializer.ReadNonEntityValueAsync(
                        payloadTypeName : typeName as string,
                        expectedValueTypeReference : null,
                        propertyAndAnnotationCollector : null,
                        collectionValidator : null,
                        validateNullValue : false,
                        isTopLevelPropertyValue : false,
                        insideResourceValue : false,
                        propertyName : propertyName).ConfigureAwait(false);

                    error.GetInstanceAnnotations().Add(new ODataInstanceAnnotation(propertyName, value.ToODataValue()));
                }
                else
                {
                    // We only allow a 'code', 'message', 'target', 'details, and 'innererror' properties
                    // in the value of the 'error' property or custom instance annotations
                    throw new ODataException(Strings.ODataJsonLightErrorDeserializer_TopLevelErrorValueWithInvalidProperty(propertyName));
                }

                break;
            }
        }