Exemple #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)
        {
            DebugUtils.CheckNoExternalCallers();
            string valueRead = this.JsonReader.ReadStringValue(annotationName);

            ODataJsonLightReaderUtils.ValidateAnnotationStringValue(valueRead, annotationName);
            return(valueRead);
        }
Exemple #2
0
        /// <summary>
        /// Reads and validates a string value from the json reader and processes it as a long.
        /// </summary>
        /// <param name="annotationName">The name of the annotation being read (used for error reporting).</param>
        /// <returns>The long that was read.</returns>
        internal long ReadAndValidateAnnotationStringValueAsLong(string annotationName)
        {
            DebugUtils.CheckNoExternalCallers();
            string stringValue = this.ReadAndValidateAnnotationStringValue(annotationName);

            return((long)ODataJsonLightReaderUtils.ConvertValue(
                       stringValue,
                       EdmCoreModel.Instance.GetInt64(false),
                       this.MessageReaderSettings,
                       this.Version,
                       /*validateNullValue*/ true,
                       annotationName));
        }
        /// <summary>
        /// Reads a property value which occurs in the "odata.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">DuplicatePropertyNamesChecker 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 "odata.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, DuplicatePropertyNamesChecker duplicationPropertyNameChecker)
        {
            switch (propertyName)
            {
            case JsonConstants.ODataErrorCodeName:
                error.ErrorCode = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorCodeName);
                break;

            case JsonConstants.ODataErrorMessageName:
                this.ReadErrorMessageObject(error);
                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;

                    var odataAnnotations = duplicationPropertyNameChecker.GetODataPropertyAnnotations(propertyName);
                    if (odataAnnotations != null)
                    {
                        odataAnnotations.TryGetValue(ODataAnnotationNames.ODataType, out typeName);
                    }

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

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

                break;
            }
        }
Exemple #4
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="duplicatePropertyNamesChecker">The duplicate property names checker.</param>
        /// <param name="readPropertyAnnotationValue">Callback to read the property annotation value.</param>
        private void ProcessPropertyAnnotation(string annotatedPropertyName, string annotationName, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, 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(OData.Strings.ODataJsonLightDeserializer_OnlyODataTypeAnnotationCanTargetInstanceAnnotation(annotationName, annotatedPropertyName, ODataAnnotationNames.ODataType));
            }

            // Read over the property name.
            this.JsonReader.Read();
            if (ODataJsonLightReaderUtils.IsODataAnnotationName(annotationName))
            {
                // OData annotations are read.
                duplicatePropertyNamesChecker.AddODataPropertyAnnotation(annotatedPropertyName, annotationName, readPropertyAnnotationValue(annotationName));
            }
            else
            {
                // All other property annotations are ignored.
                duplicatePropertyNamesChecker.AddCustomPropertyAnnotation(annotatedPropertyName, annotationName);
                this.JsonReader.SkipValue();
            }
        }
        /// <summary>
        /// Reads a property value which occurs in the "message" 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 propety whose value is to be read.</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 "message" object.
        ///                 any                         - Anything else after the property value is an invalid payload (but won't fail in this method).
        /// </remarks>
        private void ReadPropertyValueInMessageObject(ODataError error, string propertyName)
        {
            switch (propertyName)
            {
            case JsonConstants.ODataErrorMessageLanguageName:
                error.MessageLanguage = this.JsonReader.ReadStringValue(JsonConstants.ODataErrorMessageLanguageName);
                break;

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

            default:
                if (ODataJsonLightReaderUtils.IsAnnotationProperty(propertyName))
                {
                    // ignore custom instance annotations
                    this.JsonReader.SkipValue();
                    break;
                }

                // we only allow a 'lang' and 'value' properties in the value of the 'message' property
                throw new ODataException(Strings.ODataJsonErrorDeserializer_TopLevelErrorMessageValueWithInvalidProperty(propertyName));
            }
        }
Exemple #6
0
        /// <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 metadata URI and parsed it, look at the detected payload kind and return it.
            if (this.MetadataUriParseResult != null)
            {
                // Store the parsed metadata URI on the input context so we can avoid parsing it again.
                detectionInfo.SetPayloadKindDetectionFormatState(new ODataJsonLightPayloadKindDetectionState(this.MetadataUriParseResult));

                return(this.MetadataUriParseResult.DetectedPayloadKinds);
            }

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

            while (this.JsonReader.NodeType == JsonNodeType.Property)
            {
                string propertyName = this.JsonReader.ReadPropertyName();
                if (!ODataJsonLightReaderUtils.IsAnnotationProperty(propertyName))
                {
                    // If we find a non-annotation property, this is not an error payload
                    return(Enumerable.Empty <ODataPayloadKind>());
                }

                string annotatedPropertyName, annotationName;
                if (!ODataJsonLightDeserializer.TryParsePropertyAnnotation(propertyName, out annotatedPropertyName, out annotationName))
                {
                    // Instance annotation; check for odata.error
                    if (ODataJsonLightReaderUtils.IsODataAnnotationName(propertyName))
                    {
                        if (string.CompareOrdinal(ODataAnnotationNames.ODataError, 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 odata.error property.
                            // Skip the error value and check whether there are more properties.
                            this.JsonReader.SkipValue();
                        }
                        else
                        {
                            // Any odata.* instance annotations other than odata.error are not allowed for errors.
                            return(Enumerable.Empty <ODataPayloadKind>());
                        }
                    }
                    else
                    {
                        // Skip custom instance annotations
                        this.JsonReader.SkipValue();
                    }
                }
                else
                {
                    // Property annotation; not allowed for errors
                    return(Enumerable.Empty <ODataPayloadKind>());
                }
            }

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

            return(new ODataPayloadKind[] { ODataPayloadKind.Error });
        }
Exemple #7
0
        /// <summary>
        /// Writes an annotation group declaration or annotation group reference if specified for the entry.
        /// </summary>
        /// <param name="entry">The entry to write the annotation group declaration or reference for.</param>
        internal void WriteAnnotationGroup(ODataEntry entry)
        {
            DebugUtils.CheckNoExternalCallers();
            Debug.Assert(entry != null, "entry != null");

            ODataJsonLightAnnotationGroup annotationGroup = entry.GetAnnotation <ODataJsonLightAnnotationGroup>();

            if (annotationGroup == null)
            {
                return;
            }

            if (!this.JsonLightOutputContext.WritingResponse)
            {
                throw new ODataException(OData.Strings.ODataJsonLightEntryAndFeedSerializer_AnnotationGroupInRequest);
            }

            string annotationGroupName = annotationGroup.Name;

            if (string.IsNullOrEmpty(annotationGroupName))
            {
                throw new ODataException(OData.Strings.ODataJsonLightEntryAndFeedSerializer_AnnotationGroupWithoutName);
            }

            // Check whether this is the first occurrence of the annotation group.
            ODataJsonLightAnnotationGroup existingAnnotationGroup;

            if (this.annotationGroups.TryGetValue(annotationGroupName, out existingAnnotationGroup))
            {
                // Make sure the annotation groups are reference equal if they have the same name.
                if (!object.ReferenceEquals(existingAnnotationGroup, annotationGroup))
                {
                    throw new ODataException(OData.Strings.ODataJsonLightEntryAndFeedSerializer_DuplicateAnnotationGroup(annotationGroupName));
                }

                // Write an annotation group reference
                this.JsonWriter.WriteName(ODataAnnotationNames.ODataAnnotationGroupReference);
                this.JsonWriter.WritePrimitiveValue(annotationGroupName, this.JsonLightOutputContext.Version);
            }
            else
            {
                // Write an annotation group declaration
                this.JsonWriter.WriteName(ODataAnnotationNames.ODataAnnotationGroup);
                this.JsonWriter.StartObjectScope();
                this.JsonWriter.WriteName(JsonLightConstants.ODataAnnotationGroupNamePropertyName);
                this.JsonWriter.WritePrimitiveValue(annotationGroupName, this.JsonLightOutputContext.Version);

                if (annotationGroup.Annotations != null)
                {
                    foreach (KeyValuePair <string, object> kvp in annotationGroup.Annotations)
                    {
                        string annotationKey = kvp.Key;
                        Debug.Assert(annotationKey != null, "annotationKey != null");
                        if (annotationKey.Length == 0)
                        {
                            throw new ODataException(OData.Strings.ODataJsonLightEntryAndFeedSerializer_AnnotationGroupMemberWithoutName(annotationGroup.Name));
                        }

                        if (!ODataJsonLightReaderUtils.IsAnnotationProperty(annotationKey))
                        {
                            throw new ODataException(OData.Strings.ODataJsonLightEntryAndFeedSerializer_AnnotationGroupMemberMustBeAnnotation(annotationKey, annotationGroup.Name));
                        }

                        this.JsonWriter.WriteName(annotationKey);

                        object annotationValue       = kvp.Value;
                        string annotationValueString = annotationValue as string;
                        if (annotationValueString == null && annotationValue != null)
                        {
                            throw new ODataException(OData.Strings.ODataJsonLightEntryAndFeedSerializer_AnnotationGroupMemberWithInvalidValue(annotationKey, annotationGroup.Name, annotationValue.GetType().FullName));
                        }

                        this.JsonWriter.WritePrimitiveValue(annotationValueString, this.JsonLightOutputContext.Version);
                    }
                }

                this.JsonWriter.EndObjectScope();

                // Remember that we wrote the declaration of the annotation group.
                this.annotationGroups.Add(annotationGroupName, annotationGroup);
            }
        }
Exemple #8
0
        /// <summary>
        /// Parses JSON object property starting with the current position of the JSON reader.
        /// </summary>
        /// <param name="duplicatePropertyNamesChecker">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(
            DuplicatePropertyNamesChecker duplicatePropertyNamesChecker,
            Func <string, object> readPropertyAnnotationValue,
            out string parsedPropertyName)
        {
            Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != 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);
                propertyNameFromReader = propertyNameFromReader ?? 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(OData.Strings.ODataJsonLightDeserializer_AnnotationTargetingInstanceAnnotationWithoutValue(lastPropertyAnnotationNameFound, parsedPropertyName));
                    }

                    return(PropertyParsingResult.PropertyWithoutValue);
                }

                duplicatePropertyNamesChecker.AnnotationCollector.ShouldCollectAnnotation =
                    (this.MessageReaderSettings.UndeclaredPropertyBehaviorKinds
                     == ODataUndeclaredPropertyBehaviorKinds.SupportUndeclaredValueProperty);
                string skippedRawJson = null;
                if (isPropertyAnnotation)
                {
                    duplicatePropertyNamesChecker.AnnotationCollector.TryPeekAndCollectAnnotationRawJson(
                        this.JsonReader, propertyNameFromReader, annotationNameFromReader);

                    // If this is a unknown odata annotation targeting a property, we skip over it. See remark on the method SkippedOverUnknownODataAnnotation() for detailed explaination.
                    // 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 skippedRawJson))
                    {
                        continue;
                    }

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

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

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

                // We are encountering the property name for the first time.
                // Read over the property name.
                this.JsonReader.Read();
                parsedPropertyName = propertyNameFromReader;

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

                if (!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");
                duplicatePropertyNamesChecker.AnnotationCollector.TryPeekAndCollectAnnotationRawJson(
                    this.JsonReader, "", propertyNameFromReader); // propertyNameFromReader is the annotation name

                // Handle 'odata.XXXXX' annotations
                if (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(OData.Strings.ODataJsonLightDeserializer_AnnotationTargetingInstanceAnnotationWithoutValue(lastPropertyAnnotationNameFound, parsedPropertyName));
                }

                return(PropertyParsingResult.PropertyWithoutValue);
            }

            return(PropertyParsingResult.EndOfObject);
        }