/// <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="handleProperty">Function callback to handle to resule of parse property.</param> internal void ProcessProperty( DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, Func <string, object> readPropertyAnnotationValue, Action <PropertyParsingResult, string> handleProperty) { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); Debug.Assert(readPropertyAnnotationValue != null, "readPropertyAnnotationValue != null"); Debug.Assert(handleProperty != null, "handleProperty != null"); this.AssertJsonCondition(JsonNodeType.Property); string propertyName; PropertyParsingResult propertyParsingResult = this.ParseProperty(duplicatePropertyNamesChecker, readPropertyAnnotationValue, out propertyName); while (propertyParsingResult == PropertyParsingResult.CustomInstanceAnnotation && this.ShouldSkipCustomInstanceAnnotation(propertyName)) { // Make sure there's no duplicated instance annotation name even though we are skipping over it. duplicatePropertyNamesChecker.MarkPropertyAsProcessed(propertyName); // Skip over the instance annotation value and don't report it to the OM. this.JsonReader.SkipValue(); propertyParsingResult = this.ParseProperty(duplicatePropertyNamesChecker, readPropertyAnnotationValue, out propertyName); } handleProperty(propertyParsingResult, propertyName); if (propertyParsingResult != PropertyParsingResult.EndOfObject) { duplicatePropertyNamesChecker.MarkPropertyAsProcessed(propertyName); } }
public void DuplicateInstanceCustomAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.MarkPropertyAsProcessed("custom.name"); action.ShouldNotThrow(); action.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationNotAllowed("custom.name")); }
public void DuplicateInstanceCustomAnnotationShouldFail() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); Action action = () => duplicateChecker.MarkPropertyAsProcessed("custom.name"); action.ShouldNotThrow(); action.ShouldThrow <ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_DuplicateAnnotationNotAllowed("custom.name")); }
public void MarkPropertyAsProcessedWithNoPropertyShouldWork() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); duplicateChecker.MarkPropertyAsProcessed("property"); Action odataAction = () => duplicateChecker.AddODataPropertyAnnotation("property", JsonLightConstants.ODataAnnotationNamespacePrefix + "name", null); odataAction.ShouldThrow <ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_PropertyAnnotationAfterTheProperty(JsonLightConstants.ODataAnnotationNamespacePrefix + "name", "property")); Action customAction = () => duplicateChecker.AddCustomPropertyAnnotation("property", "custom.name"); customAction.ShouldThrow <ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_PropertyAnnotationAfterTheProperty("custom.name", "property")); }
/// <summary> /// Reads the odata.context annotation. /// </summary> /// <param name="payloadKind">The payload kind for which to read the context URI.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker.</param> /// <param name="failOnMissingContextUriAnnotation">true if the method should fail if the context URI annotation is missing, false if that can be ignored.</param> /// <returns>The value of the context URI annotation.</returns> internal string ReadContextUriAnnotation( ODataPayloadKind payloadKind, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool failOnMissingContextUriAnnotation) { if (this.JsonReader.NodeType != JsonNodeType.Property) { if (!failOnMissingContextUriAnnotation || payloadKind == ODataPayloadKind.Unsupported) { // Do not fail during payload kind detection return(null); } throw new ODataException(OData.Core.Strings.ODataJsonLightDeserializer_ContextLinkNotFoundAsFirstProperty); } // Must make sure the input odata.context has a '@' prefix string propertyName = this.JsonReader.GetPropertyName(); if (string.CompareOrdinal(JsonLightConstants.ODataPropertyAnnotationSeparatorChar + ODataAnnotationNames.ODataContext, propertyName) != 0 && !this.CompareSimplifiedODataAnnotation(JsonLightConstants.SimplifiedODataContextPropertyName, propertyName)) { if (!failOnMissingContextUriAnnotation || payloadKind == ODataPayloadKind.Unsupported) { // Do not fail during payload kind detection return(null); } throw new ODataException(OData.Core.Strings.ODataJsonLightDeserializer_ContextLinkNotFoundAsFirstProperty); } if (duplicatePropertyNamesChecker != null) { duplicatePropertyNamesChecker.MarkPropertyAsProcessed(propertyName); } // Read over the property name this.JsonReader.ReadNext(); return(this.JsonReader.ReadStringValue()); }
/// <summary> /// Reads the odata.metadata annotation. /// </summary> /// <param name="payloadKind">The payload kind for which to read the metadata URI.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker.</param> /// <param name="failOnMissingMetadataUriAnnotation">true if the method should fail if the metadata URI annotation is missing, false if that can be ignored.</param> /// <returns>The value of the metadata URI annotation.</returns> private string ReadMetadataUriAnnotation( ODataPayloadKind payloadKind, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool failOnMissingMetadataUriAnnotation) { if (this.JsonReader.NodeType != JsonNodeType.Property) { if (!failOnMissingMetadataUriAnnotation || payloadKind == ODataPayloadKind.Unsupported) { // Do not fail during payload kind detection return(null); } throw new ODataException(OData.Strings.ODataJsonLightDeserializer_MetadataLinkNotFoundAsFirstProperty); } string propertyName = this.JsonReader.GetPropertyName(); if (string.CompareOrdinal(ODataAnnotationNames.ODataMetadata, propertyName) != 0) { if (!failOnMissingMetadataUriAnnotation || payloadKind == ODataPayloadKind.Unsupported) { // Do not fail during payload kind detection return(null); } throw new ODataException(OData.Strings.ODataJsonLightDeserializer_MetadataLinkNotFoundAsFirstProperty); } if (duplicatePropertyNamesChecker != null) { duplicatePropertyNamesChecker.MarkPropertyAsProcessed(propertyName); } // Read over the property name this.JsonReader.ReadNext(); return(this.JsonReader.ReadStringValue()); }
/// <summary> /// Detects whether we are currently reading a complex property or not. This can be determined from metadata (if we have it) /// or from the presence of the odata.type instance annotation in the payload. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker in use for the entry content.</param> /// <param name="expectedPropertyTypeReference">The expected type reference of the property to read.</param> /// <param name="payloadTypeName">The type name of the complex value if found in the payload; otherwise null.</param> /// <returns>true if we are reading a complex property; otherwise false.</returns> /// <remarks> /// This method does not move the reader. /// </remarks> private bool ReadingComplexProperty(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, IEdmTypeReference expectedPropertyTypeReference, out string payloadTypeName) { payloadTypeName = null; bool readingComplexProperty = false; // First try to use the metadata if is available if (expectedPropertyTypeReference != null) { readingComplexProperty = expectedPropertyTypeReference.IsComplex(); } // Then check whether the first property in the JSON object is the 'odata.type' // annotation; if we don't have an expected property type reference, the 'odata.type' // annotation has to exist for complex properties. (This can happen for top-level open // properties). if (this.JsonReader.NodeType == JsonNodeType.Property && this.TryReadODataTypeAnnotation(out payloadTypeName)) { // Register the odata.type annotation we just found with the duplicate property names checker. duplicatePropertyNamesChecker.MarkPropertyAsProcessed(ODataAnnotationNames.ODataType); IEdmType expectedPropertyType = null; if (expectedPropertyTypeReference != null) { expectedPropertyType = expectedPropertyTypeReference.Definition; } EdmTypeKind typeKind = EdmTypeKind.None; IEdmType actualWirePropertyTypeReference = MetadataUtils.ResolveTypeNameForRead( this.Model, expectedPropertyType, payloadTypeName, this.MessageReaderSettings.ReaderBehavior, out typeKind); if (actualWirePropertyTypeReference != null) { readingComplexProperty = actualWirePropertyTypeReference.IsODataComplexTypeKind(); } } return readingComplexProperty; }
/// <summary> /// Reads the payload type name from a JSON object (if it exists). /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to track the detected 'odata.type' annotation (if any).</param> /// <param name="insideComplexValue">true if we are reading a complex value and the reader is already positioned inside the complex value; otherwise false.</param> /// <param name="payloadTypeName">The value of the odata.type annotation or null if no such annotation exists.</param> /// <returns>true if a type name was read from the payload; otherwise false.</returns> /// <remarks> /// Precondition: StartObject the start of a JSON object /// Postcondition: Property the first property of the object if no 'odata.type' annotation exists as first property /// or the first property after the 'odata.type' annotation. /// EndObject for an empty JSON object or an object with only the 'odata.type' annotation /// </remarks> private bool TryReadPayloadTypeFromObject(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool insideComplexValue, out string payloadTypeName) { Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); Debug.Assert( (this.JsonReader.NodeType == JsonNodeType.StartObject && !insideComplexValue) || ((this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject) && insideComplexValue), "Pre-Condition: JsonNodeType.StartObject when not inside complex value; JsonNodeType.Property or JsonNodeType.EndObject otherwise."); bool readTypeName = false; payloadTypeName = null; // If not already positioned inside the JSON object, read over the object start if (!insideComplexValue) { this.JsonReader.ReadStartObject(); } if (this.JsonReader.NodeType == JsonNodeType.Property) { readTypeName = this.TryReadODataTypeAnnotation(out payloadTypeName); if (readTypeName) { // Register the odata.type annotation we just found with the duplicate property names checker. duplicatePropertyNamesChecker.MarkPropertyAsProcessed(ODataAnnotationNames.ODataType); } } this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); return readTypeName; }
public void MarkPropertyAsProcessedWithNoPropertyShouldWork() { DuplicatePropertyNamesChecker duplicateChecker = new DuplicatePropertyNamesChecker(false, true); duplicateChecker.MarkPropertyAsProcessed("property"); Action odataAction = () => duplicateChecker.AddODataPropertyAnnotation("property", JsonLightConstants.ODataAnnotationNamespacePrefix + "name", null); odataAction.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_PropertyAnnotationAfterTheProperty(JsonLightConstants.ODataAnnotationNamespacePrefix + "name", "property")); Action customAction = () => duplicateChecker.AddCustomPropertyAnnotation("property", "custom.name"); customAction.ShouldThrow<ODataException>().WithMessage(ErrorStrings.DuplicatePropertyNamesChecker_PropertyAnnotationAfterTheProperty("custom.name", "property")); }