/// <summary> /// Creates a text ATOM value. /// </summary> /// <param name="textValue">The text value to use.</param> /// <param name="contentKind">The content kind of the value.</param> /// <returns>The Atom text value.</returns> private static AtomTextConstruct CreateAtomTextConstruct(string textValue, SyndicationTextContentKind contentKind) { AtomTextConstructKind kind; switch (contentKind) { case SyndicationTextContentKind.Plaintext: kind = AtomTextConstructKind.Text; break; case SyndicationTextContentKind.Html: kind = AtomTextConstructKind.Html; break; case SyndicationTextContentKind.Xhtml: kind = AtomTextConstructKind.Xhtml; break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_CreateAtomTextConstruct)); } return(new AtomTextConstruct { Kind = kind, Text = textValue, }); }
/// <summary> /// Gets the correct set of function signatures for type promotion for a given binary operator. /// </summary> /// <param name="operatorKind">The operator kind to get the signatures for.</param> /// <returns>The set of signatures for the specified <paramref name="operatorKind"/>.</returns> private static FunctionSignature[] GetFunctionSignatures(BinaryOperatorKind operatorKind) { switch (operatorKind) { case BinaryOperatorKind.Or: // fall through case BinaryOperatorKind.And: return(logicalSignatures); case BinaryOperatorKind.Equal: // fall through case BinaryOperatorKind.NotEqual: // fall through case BinaryOperatorKind.GreaterThan: // fall through case BinaryOperatorKind.GreaterThanOrEqual: // fall through case BinaryOperatorKind.LessThan: // fall through case BinaryOperatorKind.LessThanOrEqual: return(relationalSignatures); case BinaryOperatorKind.Add: // fall through case BinaryOperatorKind.Subtract: // fall through case BinaryOperatorKind.Multiply: // fall through case BinaryOperatorKind.Divide: // fall through case BinaryOperatorKind.Modulo: // fall through return(arithmeticSignatures); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.TypePromotionUtils_GetFunctionSignatures_Binary_UnreachableCodepath)); } }
/// <summary> /// Compute the result type of a binary operator based on the type of its operands and the operator kind. /// </summary> /// <param name="typeReference">The type reference of the operators.</param> /// <param name="operatorKind">The kind of operator.</param> /// <returns>The result type reference of the binary operator.</returns> internal static IEdmPrimitiveTypeReference GetBinaryOperatorResultType(IEdmPrimitiveTypeReference typeReference, BinaryOperatorKind operatorKind) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(typeReference != null, "type != null"); switch (operatorKind) { case BinaryOperatorKind.Or: // fall through case BinaryOperatorKind.And: // fall through case BinaryOperatorKind.Equal: // fall through case BinaryOperatorKind.NotEqual: // fall through case BinaryOperatorKind.GreaterThan: // fall through case BinaryOperatorKind.GreaterThanOrEqual: // fall through case BinaryOperatorKind.LessThan: // fall through case BinaryOperatorKind.LessThanOrEqual: return(EdmCoreModel.Instance.GetBoolean(typeReference.IsNullable)); case BinaryOperatorKind.Add: // fall through case BinaryOperatorKind.Subtract: // fall through case BinaryOperatorKind.Multiply: // fall through case BinaryOperatorKind.Divide: // fall through case BinaryOperatorKind.Modulo: return(typeReference); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.QueryNodeUtils_BinaryOperatorResultType_UnreachableCodepath)); } }
/// <summary> /// Reads EPM values from a person construct (author or contributor). /// </summary> /// <param name="targetList">The target list, this can be either a list of properties (on entry or complex value), /// or a list of items (for a collection of primitive types).</param> /// <param name="targetTypeReference">The type of the value on which to set the property (can be entity, complex or primitive).</param> /// <param name="targetSegment">The target segment which points to either author or contributor element.</param> /// <param name="personMetadata">The person ATOM metadata to read from.</param> private void ReadPersonEpm(ReadOnlyEnumerable <ODataProperty> targetList, IEdmTypeReference targetTypeReference, EpmTargetPathSegment targetSegment, AtomPersonMetadata personMetadata) { Debug.Assert(targetList != null, "targetList != null"); Debug.Assert(targetTypeReference != null, "targetTypeReference != null"); Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert( targetSegment.SegmentName == AtomConstants.AtomAuthorElementName || targetSegment.SegmentName == AtomConstants.AtomContributorElementName, "targetSegment must be author or contributor."); Debug.Assert(personMetadata != null, "personMetadata != null"); foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments) { Debug.Assert(subSegment.HasContent, "sub segment of author segment must have content, there are no subsegments which don't have content under author."); Debug.Assert( subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null && subSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty, "We should never find a subsegment without EPM attribute or for custom mapping when writing syndication person EPM."); switch (subSegment.EpmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.AuthorName: case SyndicationItemProperty.ContributorName: // Note that person name can never specify true null in EPM, since it can't have the m:null attribute on it. string personName = personMetadata.Name; if (personName != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personName); } break; case SyndicationItemProperty.AuthorEmail: case SyndicationItemProperty.ContributorEmail: string personEmail = personMetadata.Email; if (personEmail != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personEmail); } break; case SyndicationItemProperty.AuthorUri: case SyndicationItemProperty.ContributorUri: string personUri = personMetadata.UriFromEpm; if (personUri != null) { this.SetEpmValue(targetList, targetTypeReference, subSegment.EpmInfo, personUri); } break; default: // Unhandled EpmTargetPathSegment.SegmentName. throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadPersonEpm)); } } }
/// <summary> /// Gets the payload type name for an OData OM instance for JsonLight. /// </summary> /// <param name="payloadItem">The payload item to get the type name for.</param> /// <returns>The type name as read from the payload item (or constructed for primitive items).</returns> internal static string GetPayloadTypeName(object payloadItem) { DebugUtils.CheckNoExternalCallers(); if (payloadItem == null) { return(null); } TypeCode typeCode = ODataPlatformHelper.GetTypeCode(payloadItem.GetType()); switch (typeCode) { // In JSON only boolean, DateTime, String, Int32 and Double are recognized as primitive types // (without additional type conversion). So only check for those; if not one of these primitive // types it must be a complex, entity or collection value. case TypeCode.Boolean: return(Metadata.EdmConstants.EdmBooleanTypeName); case TypeCode.DateTime: return(Metadata.EdmConstants.EdmDateTimeTypeName); case TypeCode.String: return(Metadata.EdmConstants.EdmStringTypeName); case TypeCode.Int32: return(Metadata.EdmConstants.EdmInt32TypeName); case TypeCode.Double: return(Metadata.EdmConstants.EdmDoubleTypeName); default: Debug.Assert(typeCode == TypeCode.Object, "If not one of the primitive types above, it must be an object in JSON."); break; } ODataComplexValue complexValue = payloadItem as ODataComplexValue; if (complexValue != null) { return(complexValue.TypeName); } ODataCollectionValue collectionValue = payloadItem as ODataCollectionValue; if (collectionValue != null) { return(collectionValue.TypeName); } ODataEntry entry = payloadItem as ODataEntry; if (entry != null) { return(entry.TypeName); } throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightReader_ReadEntryStart)); }
/// <summary> /// Gets the correct set of function signatures for type promotion for a given binary operator. /// </summary> /// <param name="operatorKind">The operator kind to get the signatures for.</param> /// <returns>The set of signatures for the specified <paramref name="operatorKind"/>.</returns> private static FunctionSignature[] GetFunctionSignatures(UnaryOperatorKind operatorKind) { switch (operatorKind) { case UnaryOperatorKind.Negate: return(negationSignatures); case UnaryOperatorKind.Not: return(notSignatures); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.TypePromotionUtils_GetFunctionSignatures_Unary_UnreachableCodepath)); } }
/// <summary> /// Reads the end of a collection; this includes collection-level instance annotations. /// </summary> /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param> /// <remarks> /// Pre-Condition: EndArray node: End of the collection content array /// Post-Condition: EndOfInput: All of the collection payload has been consumed. /// </remarks> internal void ReadCollectionEnd(bool isReadingNestedPayload) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "Pre-condition: JsonNodeType.EndArray"); this.JsonReader.AssertNotBuffering(); this.JsonReader.ReadEndArray(); if (!isReadingNestedPayload) { // Create a new duplicate property names checker object here; we don't have to use the one from reading the // collection start since we don't allow any annotations/properties after the collection property. DuplicatePropertyNamesChecker collectionEndDuplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); // Fail on anything after the collection that is not a custom instance annotation while (this.JsonReader.NodeType == JsonNodeType.Property) { this.ProcessProperty( collectionEndDuplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { // This method will allow and skip over any custom annotations, but will not report them as enum values, so any result we get other than EndOfObject indicates a malformed payload. switch (propertyParsingResult) { case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.ODataInstanceAnnotation: // fall through case PropertyParsingResult.PropertyWithoutValue: // fall through case PropertyParsingResult.PropertyWithValue: // fall through case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionEnd(propertyName)); case PropertyParsingResult.EndOfObject: break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionEnd)); } }); } // read the end-object node of the value containing the 'value' property this.JsonReader.ReadEndObject(); } }
/// <summary> /// Parses the eq, ne, lt, gt, le, ge operators. /// </summary> /// <returns>The lexical token representing the expression.</returns> private QueryToken ParseComparison() { this.RecurseEnter(); QueryToken left = this.ParseAdditive(); while (this.lexer.CurrentToken.IsComparisonOperator) { BinaryOperatorKind binaryOperatorKind; switch (this.lexer.CurrentToken.Text) { case ExpressionConstants.KeywordEqual: binaryOperatorKind = BinaryOperatorKind.Equal; break; case ExpressionConstants.KeywordNotEqual: binaryOperatorKind = BinaryOperatorKind.NotEqual; break; case ExpressionConstants.KeywordGreaterThan: binaryOperatorKind = BinaryOperatorKind.GreaterThan; break; case ExpressionConstants.KeywordGreaterThanOrEqual: binaryOperatorKind = BinaryOperatorKind.GreaterThanOrEqual; break; case ExpressionConstants.KeywordLessThan: binaryOperatorKind = BinaryOperatorKind.LessThan; break; case ExpressionConstants.KeywordLessThanOrEqual: binaryOperatorKind = BinaryOperatorKind.LessThanOrEqual; break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriQueryExpressionParser_ParseComparison)); } this.lexer.NextToken(); QueryToken right = this.ParseAdditive(); left = new BinaryOperatorToken(binaryOperatorKind, left, right); } this.RecurseLeave(); return(left); }
/// <summary> /// Writes a non-leaf segment which has sub segments. /// </summary> /// <param name="targetSegment">The segment being written</param> /// <param name="epmValueCache">EPM value cache to use to get property values, or a primitive value</param> /// <param name="typeReference">The type of the entry or collection item.</param> private void WriteParentSegment(EpmTargetPathSegment targetSegment, object epmValueCache, IEdmTypeReference typeReference) { Debug.Assert(targetSegment != null, "targetSegment != null"); if (targetSegment.SegmentName == AtomConstants.AtomAuthorElementName) { AtomPersonMetadata authorMetadata = this.WritePersonEpm(targetSegment, epmValueCache, typeReference); if (authorMetadata != null) { List <AtomPersonMetadata> authors = (List <AtomPersonMetadata>) this.entryMetadata.Authors; if (authors == null) { authors = new List <AtomPersonMetadata>(); this.entryMetadata.Authors = authors; } authors.Add(authorMetadata); } } else if (targetSegment.SegmentName == AtomConstants.AtomContributorElementName) { AtomPersonMetadata contributorMetadata = this.WritePersonEpm(targetSegment, epmValueCache, typeReference); if (contributorMetadata != null) { List <AtomPersonMetadata> contributors = (List <AtomPersonMetadata>) this.entryMetadata.Contributors; if (contributors == null) { contributors = new List <AtomPersonMetadata>(); this.entryMetadata.Contributors = contributors; } contributors.Add(contributorMetadata); } } else { // Unhandled EpmTargetPathSegment.SegmentName. throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WriteParentSegment_TargetSegmentName)); } }
/// <summary> /// Write the unary token as URI part to this builder. /// </summary> /// <param name="unary">To write as URI part.</param> private void WriteUnary(UnaryOperatorToken unary) { ExceptionUtils.CheckArgumentNotNull(unary, "unary"); switch (unary.OperatorKind) { case UnaryOperatorKind.Negate: this.builder.Append(ExpressionConstants.SymbolNegate); break; case UnaryOperatorKind.Not: this.builder.Append(ExpressionConstants.KeywordNot); this.builder.Append(ExpressionConstants.SymbolEscapedSpace); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataUriBuilder_WriteUnary_UnreachableCodePath)); } this.WriteQuery(unary.Operand); }
/// <summary> /// Reads a non-leaf segment which has sub segments. /// </summary> /// <param name="targetSegment">The segment being read.</param> /// <param name="entryMetadata">The ATOM entry metadata to read from.</param> private void ReadParentSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata) { Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert(entryMetadata != null, "entryMetadata != null"); switch (targetSegment.SegmentName) { case AtomConstants.AtomAuthorElementName: // If a singleton property (non-collection) is mapped to author and there are multiple authors // EPM uses the first author. AtomPersonMetadata authorMetadata = entryMetadata.Authors.FirstOrDefault(); if (authorMetadata != null) { this.ReadPersonEpm( this.EntryState.Entry.Properties.ToReadOnlyEnumerable("Properties"), this.EntryState.EntityType.ToTypeReference(), targetSegment, authorMetadata); } break; case AtomConstants.AtomContributorElementName: // If a singleton property (non-collection) is mapped to contributor and there are multiple contributors // EPM uses the first contributor. AtomPersonMetadata contributorMetadata = entryMetadata.Contributors.FirstOrDefault(); if (contributorMetadata != null) { this.ReadPersonEpm( this.EntryState.Entry.Properties.ToReadOnlyEnumerable("Properties"), this.EntryState.EntityType.ToTypeReference(), targetSegment, contributorMetadata); } break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadParentSegment_TargetSegmentName)); } }
/// <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); }
private void SetEpmValueForSegment( EntityPropertyMappingInfo epmInfo, int propertyValuePathIndex, IEdmStructuredTypeReference segmentStructuralTypeReference, ReadOnlyEnumerable <ODataProperty> existingProperties, object propertyValue) { Debug.Assert(epmInfo != null, "epmInfo != null"); Debug.Assert(propertyValuePathIndex < epmInfo.PropertyValuePath.Length, "The propertyValuePathIndex is out of bounds."); Debug.Assert(existingProperties != null, "existingProperties != null"); string propertyName = epmInfo.PropertyValuePath[propertyValuePathIndex].PropertyName; // Do not set out-of-content values if the EPM is defined as KeepInContent=true. if (epmInfo.Attribute.KeepInContent) { return; } // Try to find the property in the existing properties // If the property value is atomic from point of view of EPM (non-streaming collection or primitive) then if it already exists // it must have been in-content, and thus we leave it as is (note that two EPMs can't map to the same property, we verify that upfront). // If the property value is non-atomic, then it is a complex value, we might want to merge the new value comming from EPM with it. ODataProperty existingProperty = existingProperties.FirstOrDefault(p => string.CompareOrdinal(p.Name, propertyName) == 0); ODataComplexValue existingComplexValue = null; if (existingProperty != null) { // In case the property exists and it's a complex value we will try to merge. // Note that if the property is supposed to be complex, but it already has a null value, then the null wins. // Since in-content null complex value wins over any EPM complex value. existingComplexValue = existingProperty.Value as ODataComplexValue; if (existingComplexValue == null) { return; } } IEdmProperty propertyMetadata = segmentStructuralTypeReference.FindProperty(propertyName); Debug.Assert(propertyMetadata != null || segmentStructuralTypeReference.IsOpen(), "We should have verified that if the property is not declared the type must be open."); // TODO: Server seems to have a bug where if there's an EPM for an open property where the EPM uses complex types (the source path is deeper than 1) // then it will actually materialize the property as entry level string property, with the value of the deep property value. If there are multiple deep // EPM for the same top-level open property it seems to set the entry level property multiple times with the values as they come from payload. // Client on the other hand doesn't have open properties, and always has a type, so no problem there. if (propertyMetadata == null && propertyValuePathIndex != epmInfo.PropertyValuePath.Length - 1) { throw new ODataException(ODataErrorStrings.EpmReader_OpenComplexOrCollectionEpmProperty(epmInfo.Attribute.SourcePath)); } // Open properties in EPM are by default of type Edm.String - there's no way to specify a typename in EPM // consumer is free to do the conversion later on if it needs to. // Note that this effectively means that ODataMessageReaderSettings.DisablePrimitiveTypeConversion is as if it's turned on for open EPM properties. IEdmTypeReference propertyType; if (propertyMetadata == null || (this.MessageReaderSettings.DisablePrimitiveTypeConversion && propertyMetadata.Type.IsODataPrimitiveTypeKind())) { propertyType = EdmCoreModel.Instance.GetString(/*nullable*/ true); } else { propertyType = propertyMetadata.Type; } // NOTE: WCF DS Server only applies the values when // - It's an open property // - It's not a key property // - It's a key property and it's a POST operation // ODataLib here will always set the property though. switch (propertyType.TypeKind()) { case EdmTypeKind.Primitive: { if (propertyType.IsStream()) { throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_StreamProperty)); } object primitiveValue; if (propertyValue == null) { ReaderValidationUtils.ValidateNullValue( this.atomInputContext.Model, propertyType, this.atomInputContext.MessageReaderSettings, /*validateNullValue*/ true, this.atomInputContext.Version, propertyName); primitiveValue = null; } else { // Convert the value to the desired target type primitiveValue = AtomValueUtils.ConvertStringToPrimitive((string)propertyValue, propertyType.AsPrimitive()); } this.AddEpmPropertyValue(existingProperties, propertyName, primitiveValue, segmentStructuralTypeReference.IsODataEntityTypeKind()); } break; case EdmTypeKind.Complex: // Note: Unlike WCF DS we don't have a preexisting instance to override (since complex values are atomic, so we should not updated them) // In our case the complex value either doesn't exist yet on the entry being reported (easy, create it) // or it exists, but then it was created during reading of previous normal or EPM properties for this entry. It never exists before // we ever get to see the entity. So in our case we will never recreate the complex value, we always start with new one // and update it with new properties as they come. (Next time we will start over with a new complex value.) Debug.Assert( existingComplexValue == null || (existingProperty != null && existingProperty.Value == existingComplexValue), "If we have existing complex value, we must have an existing property as well."); Debug.Assert( epmInfo.PropertyValuePath.Length > propertyValuePathIndex + 1, "Complex value can not be a leaf segment in the source property path. We should have failed constructing the EPM trees for it."); if (existingComplexValue == null) { Debug.Assert(existingProperty == null, "If we don't have an existing complex value, then we must not have an existing property at all."); // Create a new complex value and set its type name to the type name of the property type (in case of EPM we never have type name from the payload) existingComplexValue = new ODataComplexValue { TypeName = propertyType.ODataFullName(), Properties = new ReadOnlyEnumerable <ODataProperty>() }; this.AddEpmPropertyValue(existingProperties, propertyName, existingComplexValue, segmentStructuralTypeReference.IsODataEntityTypeKind()); } // Get the properties list of the complex value and recursively set the next EPM segment value to it. // Note that on inner complex value we don't need to check for duplicate properties // because EPM will never add a property which already exists (see the start of this method). IEdmComplexTypeReference complexPropertyTypeReference = propertyType.AsComplex(); Debug.Assert(complexPropertyTypeReference != null, "complexPropertyTypeReference != null"); this.SetEpmValueForSegment( epmInfo, propertyValuePathIndex + 1, complexPropertyTypeReference, existingComplexValue.Properties.ToReadOnlyEnumerable("Properties"), propertyValue); break; case EdmTypeKind.Collection: Debug.Assert(propertyType.IsNonEntityCollectionType(), "Collection types in EPM must be atomic."); // In this case the property value is the internal list of items. // Create a new collection value and set the list as the list of items on it. ODataCollectionValue collectionValue = new ODataCollectionValue { TypeName = propertyType.ODataFullName(), Items = new ReadOnlyEnumerable((List <object>)propertyValue) }; this.AddEpmPropertyValue(existingProperties, propertyName, collectionValue, segmentStructuralTypeReference.IsODataEntityTypeKind()); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmReader_SetEpmValueForSegment_TypeKind)); } }
/// <summary> /// Reads the primitive, complex or collection value. /// </summary> /// <param name="expectedTypeReference">The expected type reference of the value.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use (cached), or null if new one should be created.</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 a null value (i.e., throw if a null value is being written for a non-nullable property); otherwise false.</param> /// <param name="epmPresent">Whether any EPM mappings exist.</param> /// <param name="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param> /// <returns>The value read (null, primitive CLR value, ODataComplexValue or ODataCollectionValue).</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The XML element containing the value to read (also the attributes will be read from it) /// Post-Condition: XmlNodeType.EndElement - The end tag of the element. /// XmlNodeType.Element - The empty element node. /// </remarks> private object ReadNonEntityValueImplementation(IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, bool epmPresent, string propertyName) { this.AssertXmlCondition(XmlNodeType.Element); 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.XmlReader.AssertNotBuffering(); // Read the attributes looking for m:type and m:null string payloadTypeName; bool isNull; this.ReadNonEntityValueAttributes(out payloadTypeName, out isNull); object result; if (isNull) { result = this.ReadNullValue(expectedTypeReference, validateNullValue, propertyName); } else { // If we could derive the item type name from the collection's type name and no type name was specified in the payload // fill it in now. EdmTypeKind payloadTypeKind; bool derivedItemTypeNameFromCollectionTypeName = false; if (collectionValidator != null && payloadTypeName == null) { payloadTypeName = collectionValidator.ItemTypeNameFromCollection; payloadTypeKind = collectionValidator.ItemTypeKindFromCollection; derivedItemTypeNameFromCollectionTypeName = payloadTypeKind != EdmTypeKind.None; } // Resolve the payload type name and compute the target type kind and target type reference. SerializationTypeNameAnnotation serializationTypeNameAnnotation; EdmTypeKind targetTypeKind; IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*defaultPrimitivePayloadType*/ edmStringType, expectedTypeReference, payloadTypeName, this.Model, this.MessageReaderSettings, this.Version, this.GetNonEntityValueKind, out targetTypeKind, out serializationTypeNameAnnotation); if (derivedItemTypeNameFromCollectionTypeName) { Debug.Assert( serializationTypeNameAnnotation == null, "If we derived the item type name from the collection type name we must not have created a serialization type name annotation."); serializationTypeNameAnnotation = new SerializationTypeNameAnnotation { TypeName = null }; } // If we have no expected type make sure the collection items are of the same kind and specify the same name. if (collectionValidator != null) { Debug.Assert(expectedTypeReference == null, "If a collection validator is specified there must not be an expected value type reference."); collectionValidator.ValidateCollectionItem(payloadTypeName, targetTypeKind); } switch (targetTypeKind) { case EdmTypeKind.Primitive: Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type."); result = this.ReadPrimitiveValue(targetTypeReference.AsPrimitive()); break; case EdmTypeKind.Complex: Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected a complex type."); result = this.ReadComplexValue( targetTypeReference == null ? null : targetTypeReference.AsComplex(), payloadTypeName, serializationTypeNameAnnotation, duplicatePropertyNamesChecker, epmPresent); break; case EdmTypeKind.Collection: IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference); result = this.ReadCollectionValue( collectionTypeReference, payloadTypeName, serializationTypeNameAnnotation); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue)); } } this.AssertXmlCondition(true, XmlNodeType.EndElement); this.XmlReader.AssertNotBuffering(); return(result); }
/// <summary> /// Returns the 4 bits that correspond to the specified character. /// </summary> /// <param name="c">Character in the 0-F range to be converted.</param> /// <returns>The 4 bits that correspond to the specified character.</returns> /// <exception cref="FormatException">Thrown when 'c' is not in the '0'-'9','a'-'f' range.</exception> /// <remarks>This is a copy of WebConvert.HexCharToNibble.</remarks> private static byte HexCharToNibble(char c) { Debug.Assert(IsCharHexDigit(c), string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} is not a hex digit.", c)); switch (c) { case '0': return(0); case '1': return(1); case '2': return(2); case '3': return(3); case '4': return(4); case '5': return(5); case '6': return(6); case '7': return(7); case '8': return(8); case '9': return(9); case 'a': case 'A': return(10); case 'b': case 'B': return(11); case 'c': case 'C': return(12); case 'd': case 'D': return(13); case 'e': case 'E': return(14); case 'f': case 'F': return(15); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_HexCharToNibble)); } }
internal static bool TryUriStringToPrimitive(string text, IEdmTypeReference targetType, out object targetValue) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(text != null, "text != null"); Debug.Assert(targetType != null, "targetType != null"); if (targetType.IsNullable) { if (text == ExpressionConstants.KeywordNull) { targetValue = null; return(true); } } IEdmPrimitiveTypeReference primitiveTargetType = targetType.AsPrimitiveOrNull(); if (primitiveTargetType == null) { targetValue = null; return(false); } EdmPrimitiveTypeKind targetTypeKind = primitiveTargetType.PrimitiveKind(); byte[] byteArrayValue; bool binaryResult = TryUriStringToByteArray(text, out byteArrayValue); if (targetTypeKind == EdmPrimitiveTypeKind.Binary) { targetValue = (object)byteArrayValue; return(binaryResult); } else if (binaryResult) { string keyValue = Encoding.UTF8.GetString(byteArrayValue, 0, byteArrayValue.Length); return(TryUriStringToPrimitive(keyValue, targetType, out targetValue)); } else if (targetTypeKind == EdmPrimitiveTypeKind.Guid) { Guid guidValue; bool result = TryUriStringToGuid(text, out guidValue); targetValue = guidValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.DateTime) { DateTime dateTimeValue; bool result = TryUriStringToDateTime(text, out dateTimeValue); targetValue = dateTimeValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.DateTimeOffset) { DateTimeOffset dateTimeOffsetValue; bool result = TryUriStringToDateTimeOffset(text, out dateTimeOffsetValue); targetValue = dateTimeOffsetValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Time) { TimeSpan timespanValue; bool result = TryUriStringToTime(text, out timespanValue); targetValue = timespanValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Geography) { Geography geographyValue; bool result = TryUriStringToGeography(text, out geographyValue); targetValue = geographyValue; return(result); } else if (targetTypeKind == EdmPrimitiveTypeKind.Geometry) { Geometry geometryValue; bool result = TryUriStringToGeometry(text, out geometryValue); targetValue = geometryValue; return(result); } bool quoted = targetTypeKind == EdmPrimitiveTypeKind.String; if (quoted != IsUriValueQuoted(text)) { targetValue = null; return(false); } if (quoted) { text = RemoveQuotes(text); } try { switch (targetTypeKind) { case EdmPrimitiveTypeKind.String: targetValue = text; break; case EdmPrimitiveTypeKind.Boolean: targetValue = XmlConvert.ToBoolean(text); break; case EdmPrimitiveTypeKind.Byte: targetValue = XmlConvert.ToByte(text); break; case EdmPrimitiveTypeKind.SByte: targetValue = XmlConvert.ToSByte(text); break; case EdmPrimitiveTypeKind.Int16: targetValue = XmlConvert.ToInt16(text); break; case EdmPrimitiveTypeKind.Int32: targetValue = XmlConvert.ToInt32(text); break; case EdmPrimitiveTypeKind.Int64: if (TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixInt64, ref text)) { targetValue = XmlConvert.ToInt64(text); } else { targetValue = default(Int64); return(false); } break; case EdmPrimitiveTypeKind.Single: if (TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixSingle, ref text)) { targetValue = XmlConvert.ToSingle(text); } else { targetValue = default(Single); return(false); } break; case EdmPrimitiveTypeKind.Double: TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDouble, ref text); targetValue = XmlConvert.ToDouble(text); break; case EdmPrimitiveTypeKind.Decimal: if (TryRemoveLiteralSuffix(ExpressionConstants.LiteralSuffixDecimal, ref text)) { try { targetValue = XmlConvert.ToDecimal(text); } catch (FormatException) { // we need to support exponential format for decimals since we used to support them in V1 decimal result; if (Decimal.TryParse(text, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out result)) { targetValue = result; } else { targetValue = default(Decimal); return(false); } } } else { targetValue = default(Decimal); return(false); } break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.UriPrimitiveTypeParser_TryUriStringToPrimitive)); } return(true); } catch (FormatException) { targetValue = null; return(false); } catch (OverflowException) { targetValue = null; return(false); } }
/// <summary> /// Read an entity reference link. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to check for duplicate properties and /// duplicate annotations; this is a separate instance per entity reference link.</param> /// <param name="topLevel">true if we are reading a singleton entity reference link at the top level; false if we are reading /// an entity reference link as part of a collection of entity reference links.</param> /// <returns>An instance of <see cref="ODataEntityReferenceLink"/> which was read.</returns> /// <remarks> /// Pre-Condition: StartObject when the entity reference link is part of a collection /// Property the first property in the entity reference link (for a top-level link) /// EndObject the end object node of an entity reference link (for a top-level link) /// Post-Condition: EndInput for a top-level object /// EndArray for the last link in a collection of links /// Any for the first node of the next link in a collection of links /// </remarks> private ODataEntityReferenceLink ReadSingleEntityReferenceLink(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool topLevel) { this.JsonReader.AssertNotBuffering(); if (!topLevel) { if (this.JsonReader.NodeType != JsonNodeType.StartObject) { // entity reference link has to be an object throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkMustBeObjectValue(this.JsonReader.NodeType)); } this.JsonReader.ReadStartObject(); } this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); ODataEntityReferenceLink[] entityReferenceLink = { null }; // Entity reference links use instance annotations. Fail if we find a property annotation. Func <string, object> propertyAnnotationValueReader = annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLink(annotationName)); }; while (this.JsonReader.NodeType == JsonNodeType.Property) { this.ProcessProperty( duplicatePropertyNamesChecker, propertyAnnotationValueReader, (propertyParsingResult, propertyName) => { switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: // Instance annotations are not allowed for entity reference links throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidAnnotationInEntityReferenceLink(propertyName)); case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithoutValue: // If we find a property without a value it means that we did not find the 'url' property of the // entity reference link but an invalid property annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidAnnotationInEntityReferenceLink(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataEntityReferenceLinkUrlName, propertyName) != 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyInEntityReferenceLink(propertyName, JsonLightConstants.ODataEntityReferenceLinkUrlName)); } else if (entityReferenceLink[0] != null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MultipleUriPropertiesInEntityReferenceLink(JsonLightConstants.ODataEntityReferenceLinkUrlName)); } // read the value of the 'url' property string urlString = this.JsonReader.ReadStringValue(JsonLightConstants.ODataEntityReferenceLinkUrlName); if (urlString == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_EntityReferenceLinkUrlCannotBeNull(JsonLightConstants.ODataEntityReferenceLinkUrlName)); } entityReferenceLink[0] = new ODataEntityReferenceLink { Url = this.ProcessUriFromPayload(urlString) }; ReaderValidationUtils.ValidateEntityReferenceLink(entityReferenceLink[0]); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadSingleEntityReferenceLink)); } }); } if (entityReferenceLink[0] == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_MissingEntityReferenceLinkProperty(JsonLightConstants.ODataEntityReferenceLinkUrlName)); } // end of the entity reference link object this.JsonReader.ReadEndObject(); this.JsonReader.AssertNotBuffering(); return(entityReferenceLink[0]); }
/// <summary> /// Reads the entity reference link instance annotations. /// </summary> /// <param name="links">The <see cref="ODataEntityReferenceLinks"/> to read the annotations for.</param> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker for the entity reference links scope.</param> /// <param name="forLinksStart">true when parsing the instance annotations before the 'value' property; /// false when parsing the instance annotations after the 'value' property.</param> /// <remarks> /// Pre-Condition: JsonNodeType.Property The first property in the payload (or the first property after the metadata URI in responses) /// JsonNodeType.EndObject The end of the entity reference links object /// Post-Condition: JsonNodeType.EndObject When the end of the entity reference links object is reached /// Any The first node of the value of the 'url' property (if found) /// </remarks> private void ReadEntityReferenceLinksAnnotations(ODataEntityReferenceLinks links, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, bool forLinksStart) { Debug.Assert(links != null, "links != null"); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); this.JsonReader.AssertNotBuffering(); while (this.JsonReader.NodeType == JsonNodeType.Property) { // OData property annotations are not supported on entity reference links. Func <string, object> propertyAnnotationValueReader = annotationName => { throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_PropertyAnnotationForEntityReferenceLinks); }; bool foundValueProperty = false; this.ProcessProperty( duplicatePropertyNamesChecker, propertyAnnotationValueReader, (propertyParseResult, propertyName) => { switch (propertyParseResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (string.CompareOrdinal(ODataAnnotationNames.ODataNextLink, propertyName) == 0) { this.ReadEntityReferenceLinksNextLinkAnnotationValue(links); } else if (string.CompareOrdinal(ODataAnnotationNames.ODataCount, propertyName) == 0) { this.ReadEntityReferenceCountAnnotationValue(links); } else { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); } break; case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0) { // We did not find a supported link collection property; fail. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidEntityReferenceLinksPropertyFound(propertyName, JsonLightConstants.ODataValuePropertyName)); } // We found the link collection property and are done parsing property annotations; foundValueProperty = true; break; case PropertyParsingResult.PropertyWithoutValue: // If we find a property without a value it means that we did not find the entity reference links property (yet) // but an invalid property annotation throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_InvalidPropertyAnnotationInEntityReferenceLinks(propertyName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightEntityReferenceLinkDeserializer_ReadEntityReferenceLinksAnnotations)); } }); if (foundValueProperty) { return; } } if (forLinksStart) { // We did not find the 'value' property. throw new ODataException(ODataErrorStrings.ODataJsonLightEntityReferenceLinkDeserializer_ExpectedEntityReferenceLinksPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } this.AssertJsonCondition(JsonNodeType.EndObject); }
/// <summary> /// Reads the next parameter from the parameters payload. /// </summary> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker used to read a parameter payload.</param> /// <returns>true if a parameter was read from the payload; otherwise false.</returns> /// <remarks> /// Pre-Condition: Property or EndObject the property node of the parameter to read or the end object node if there are not parameters /// Post-Condition: Property or EndObject the node after the property value of a primitive, complex or null collection parameter /// Any the start of the value representing a non-null collection parameter (the collection reader will fail if this is not a StartArray node) /// </remarks> internal bool ReadNextParameter(DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); bool parameterRead = false; if (this.JsonReader.NodeType == JsonNodeType.Property) { bool foundCustomInstanceAnnotation = false; this.ProcessProperty( duplicatePropertyNamesChecker, propertyAnnotationValueReader, (propertyParsingResult, parameterName) => { switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: // OData instance annotations are not supported in parameter payloads. throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(parameterName)); case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); foundCustomInstanceAnnotation = true; break; case PropertyParsingResult.PropertyWithoutValue: throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_PropertyAnnotationWithoutPropertyForParameters(parameterName)); case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(parameterName)); case PropertyParsingResult.PropertyWithValue: IEdmTypeReference parameterTypeReference = this.parameterReader.GetParameterTypeReference(parameterName); Debug.Assert(parameterTypeReference != null, "parameterTypeReference != null"); ODataParameterReaderState state; object parameterValue; EdmTypeKind parameterTypeKind = parameterTypeReference.TypeKind(); switch (parameterTypeKind) { case EdmTypeKind.Primitive: IEdmPrimitiveTypeReference primitiveTypeReference = parameterTypeReference.AsPrimitive(); if (primitiveTypeReference.PrimitiveKind() == EdmPrimitiveTypeKind.Stream) { throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedPrimitiveParameterType(parameterName, primitiveTypeReference.PrimitiveKind())); } parameterValue = this.ReadNonEntityValue( /*payloadTypeName*/ null, primitiveTypeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, parameterName); state = ODataParameterReaderState.Value; break; case EdmTypeKind.Complex: parameterValue = this.ReadNonEntityValue( /*payloadTypeName*/ null, parameterTypeReference, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, parameterName); state = ODataParameterReaderState.Value; break; #if SUPPORT_ENTITY_PARAMETER case EdmTypeKind.Entity: parameterValue = null; state = ODataParameterReaderState.Entry; break; #endif case EdmTypeKind.Collection: parameterValue = null; if (this.JsonReader.NodeType == JsonNodeType.PrimitiveValue) { // NOTE: we support null collections in parameter payloads but nowhere else. parameterValue = this.JsonReader.ReadPrimitiveValue(); if (parameterValue != null) { throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_NullCollectionExpected(JsonNodeType.PrimitiveValue, parameterValue)); } state = ODataParameterReaderState.Value; } else if (((IEdmCollectionType)parameterTypeReference.Definition).ElementType.TypeKind() == EdmTypeKind.Entity) { #if SUPPORT_ENTITY_PARAMETER state = ODataParameterReaderState.Feed; #else throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedParameterTypeKind(parameterName, "Entity Collection")); #endif } else { state = ODataParameterReaderState.Collection; } break; default: throw new ODataException(ODataErrorStrings.ODataJsonLightParameterDeserializer_UnsupportedParameterTypeKind(parameterName, parameterTypeReference.TypeKind())); } parameterRead = true; this.parameterReader.EnterScope(state, parameterName, parameterValue); Debug.Assert( state == ODataParameterReaderState.Collection || this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject, "Expected any node for a collection; 'Property' or 'EndObject' if it is a primitive or complex value."); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightParameterDeserializer_ReadNextParameter)); } }); if (foundCustomInstanceAnnotation) { return(this.ReadNextParameter(duplicatePropertyNamesChecker)); } } if (!parameterRead && this.JsonReader.NodeType == JsonNodeType.EndObject) { this.JsonReader.ReadEndObject(); this.ReadPayloadEnd(/*isReadingNestedPayload*/ false); // Pop the scope for the previous parameter if there is any if (this.parameterReader.State != ODataParameterReaderState.Start) { this.parameterReader.PopScope(this.parameterReader.State); } // Pop the 'Start' scope and enter the 'Completed' scope this.parameterReader.PopScope(ODataParameterReaderState.Start); this.parameterReader.EnterScope(ODataParameterReaderState.Completed, /*parameterName*/ null, /*parameterValue*/ null); this.AssertJsonCondition(JsonNodeType.EndOfInput); } return(parameterRead); }
/// <summary> /// Reads a leaf segment which maps to a property value. /// </summary> /// <param name="targetSegment">The segment being read.</param> /// <param name="entryMetadata">The ATOM entry metadata to read from.</param> private void ReadPropertyValueSegment(EpmTargetPathSegment targetSegment, AtomEntryMetadata entryMetadata) { Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert(entryMetadata != null, "entryMetadata != null"); EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo; Debug.Assert( epmInfo != null && epmInfo.Attribute != null, "If the segment has content it must have EpmInfo which in turn must have the Epm attribute"); switch (epmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.Updated: // TODO: need some way to handle m:null if we decide to support it. if (this.MessageReaderSettings.ReaderBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient) { if (entryMetadata.UpdatedString != null) { this.SetEntryEpmValue(targetSegment.EpmInfo, entryMetadata.UpdatedString); } } else if (entryMetadata.Updated.HasValue) { this.SetEntryEpmValue(targetSegment.EpmInfo, XmlConvert.ToString(entryMetadata.Updated.Value)); } break; case SyndicationItemProperty.Published: // TODO: need some way to handle m:null if we decide to support it. if (this.MessageReaderSettings.ReaderBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient) { if (entryMetadata.PublishedString != null) { this.SetEntryEpmValue(targetSegment.EpmInfo, entryMetadata.PublishedString); } } else if (entryMetadata.Published.HasValue) { this.SetEntryEpmValue(targetSegment.EpmInfo, XmlConvert.ToString(entryMetadata.Published.Value)); } break; case SyndicationItemProperty.Rights: this.ReadTextConstructEpm(targetSegment, entryMetadata.Rights); break; case SyndicationItemProperty.Summary: this.ReadTextConstructEpm(targetSegment, entryMetadata.Summary); break; case SyndicationItemProperty.Title: this.ReadTextConstructEpm(targetSegment, entryMetadata.Title); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationReader_ReadEntryEpm_ContentTarget)); } }
/// <summary> /// Reads the start of a collection; this includes collection-level properties (e.g., the 'results' property) if the version permits it. /// </summary> /// <param name="collectionStartDuplicatePropertyNamesChecker">The duplicate property names checker used to keep track of the properties and annotations /// in the collection wrapper object.</param> /// <param name="isReadingNestedPayload">true if we are reading a nested collection inside a paramter payload; otherwise false.</param> /// <param name="expectedItemTypeReference">The expected item type reference or null if none is expected.</param> /// <param name="actualItemTypeReference">The validated actual item type reference (if specified in the payload) or the expected item type reference.</param> /// <returns>An <see cref="ODataCollectionStart"/> representing the collection-level information. Currently this is only the name of the collection in ATOM.</returns> /// <remarks> /// Pre-Condition: Any: the start of a nested collection value; if this is not a 'StartArray' node this method will fail. /// JsonNodeType.Property: the first property of the collection wrapper object after the metadata URI. /// JsonNodeType.EndObject: when the collection wrapper object has no properties (other than the metadata URI). /// Post-Condition: JsonNodeType.StartArray: the start of the array of the collection items. /// </remarks> internal ODataCollectionStart ReadCollectionStart( DuplicatePropertyNamesChecker collectionStartDuplicatePropertyNamesChecker, bool isReadingNestedPayload, IEdmTypeReference expectedItemTypeReference, out IEdmTypeReference actualItemTypeReference) { DebugUtils.CheckNoExternalCallers(); this.JsonReader.AssertNotBuffering(); actualItemTypeReference = expectedItemTypeReference; ODataCollectionStart collectionStart = null; if (isReadingNestedPayload) { Debug.Assert(!this.JsonLightInputContext.ReadingResponse, "Nested collections are only supported in parameter payloads in requests."); collectionStart = new ODataCollectionStart { Name = null }; } else { while (this.JsonReader.NodeType == JsonNodeType.Property) { IEdmTypeReference actualItemTypeRef = expectedItemTypeReference; this.ProcessProperty( collectionStartDuplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); case PropertyParsingResult.CustomInstanceAnnotation: this.JsonReader.SkipValue(); break; case PropertyParsingResult.PropertyWithoutValue: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_TopLevelPropertyAnnotationWithoutProperty(propertyName)); case PropertyParsingResult.PropertyWithValue: if (string.CompareOrdinal(JsonLightConstants.ODataValuePropertyName, propertyName) != 0) { throw new ODataException( ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_InvalidTopLevelPropertyName(propertyName, JsonLightConstants.ODataValuePropertyName)); } string payloadTypeName = ValidateDataPropertyTypeNameAnnotation(collectionStartDuplicatePropertyNamesChecker, propertyName); if (payloadTypeName != null) { string itemTypeName = EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); if (itemTypeName == null) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_InvalidCollectionTypeName(payloadTypeName)); } EdmTypeKind targetTypeKind; SerializationTypeNameAnnotation serializationTypeNameAnnotation; Func <EdmTypeKind> typeKindPeekedFromPayloadFunc = () => { throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart_TypeKindFromPayloadFunc)); }; actualItemTypeRef = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*defaultPrimitivePayloadType*/ null, expectedItemTypeReference, itemTypeName, this.Model, this.MessageReaderSettings, this.Version, typeKindPeekedFromPayloadFunc, out targetTypeKind, out serializationTypeNameAnnotation); } collectionStart = new ODataCollectionStart { Name = null }; break; case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightCollectionDeserializer_ReadCollectionStart)); } }); actualItemTypeReference = actualItemTypeRef; } if (collectionStart == null) { // No collection property found; there should be exactly one property in the collection wrapper that does not have a reserved name. throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_ExpectedCollectionPropertyNotFound(JsonLightConstants.ODataValuePropertyName)); } } // at this point the reader is positioned on the start array node for the collection contents if (this.JsonReader.NodeType != JsonNodeType.StartArray) { throw new ODataException(ODataErrorStrings.ODataJsonLightCollectionDeserializer_CannotReadCollectionContentStart(this.JsonReader.NodeType)); } this.JsonReader.AssertNotBuffering(); return(collectionStart); }
/// <summary> /// Writes EPM value to a person construct (author or contributor). /// </summary> /// <param name="targetSegment">The target segment which points to either author or contributor element.</param> /// <param name="epmValueCache">EPM value cache to use to get property values, or a primitive value</param> /// <param name="typeReference">The type of the entry or collection item.</param> /// <returns>The person metadata or null if no person metadata should be written for this mapping.</returns> private AtomPersonMetadata WritePersonEpm(EpmTargetPathSegment targetSegment, object epmValueCache, IEdmTypeReference typeReference) { Debug.Assert(targetSegment != null, "targetSegment != null"); Debug.Assert( targetSegment.SegmentName == AtomConstants.AtomAuthorElementName || targetSegment.SegmentName == AtomConstants.AtomContributorElementName, "targetSegment must be author or contributor."); AtomPersonMetadata personMetadata = null; foreach (EpmTargetPathSegment subSegment in targetSegment.SubSegments) { Debug.Assert(subSegment.HasContent, "sub segment of author segment must have content, there are no subsegments which don't have content under author."); Debug.Assert( subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null && subSegment.EpmInfo.Attribute.TargetSyndicationItem != SyndicationItemProperty.CustomProperty, "We should never find a subsegment without EPM attribute or for custom mapping when writing syndication person EPM."); string textPropertyValue = this.GetPropertyValueAsText(subSegment, epmValueCache, typeReference); if (textPropertyValue == null) { // In V2 we write the mapped properties always in-content when the value is null. continue; } // Initialize the person element only if we actually need to write something to it. Debug.Assert(subSegment.EpmInfo != null && subSegment.EpmInfo.Attribute != null, "The author subsegment must have EPM info and EPM attribute."); switch (subSegment.EpmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.AuthorName: case SyndicationItemProperty.ContributorName: if (textPropertyValue != null) { if (personMetadata == null) { personMetadata = new AtomPersonMetadata(); } personMetadata.Name = textPropertyValue; } break; case SyndicationItemProperty.AuthorEmail: case SyndicationItemProperty.ContributorEmail: if (textPropertyValue != null && textPropertyValue.Length > 0) { if (personMetadata == null) { personMetadata = new AtomPersonMetadata(); } personMetadata.Email = textPropertyValue; } break; case SyndicationItemProperty.AuthorUri: case SyndicationItemProperty.ContributorUri: if (textPropertyValue != null && textPropertyValue.Length > 0) { if (personMetadata == null) { personMetadata = new AtomPersonMetadata(); } personMetadata.UriFromEpm = textPropertyValue; } break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WritePersonEpm)); } } return(personMetadata); }
/// <summary> /// Writes the syndication part of EPM for an entry into ATOM metadata OM. /// </summary> /// <param name="epmValueCache">The entry properties value cache to use to access the properties.</param> /// <param name="entityType">The type of the entry.</param> /// <returns>The ATOM metadata OM with the EPM values populated.</returns> private AtomEntryMetadata WriteEntryEpm( EntryPropertiesValueCache epmValueCache, IEdmEntityTypeReference entityType) { // If there are no syndication mappings, just return null. EpmTargetPathSegment syndicationRootSegment = this.epmTargetTree.SyndicationRoot; Debug.Assert(syndicationRootSegment != null, "EPM Target tree must always have syndication root."); if (syndicationRootSegment.SubSegments.Count == 0) { return(null); } foreach (EpmTargetPathSegment targetSegment in syndicationRootSegment.SubSegments) { if (targetSegment.HasContent) { EntityPropertyMappingInfo epmInfo = targetSegment.EpmInfo; Debug.Assert( epmInfo != null && epmInfo.Attribute != null, "If the segment has content it must have EpmInfo which in turn must have the EPM attribute"); object propertyValue = this.ReadEntryPropertyValue( epmInfo, epmValueCache, entityType); string textPropertyValue = EpmWriterUtils.GetPropertyValueAsText(propertyValue); switch (epmInfo.Attribute.TargetSyndicationItem) { case SyndicationItemProperty.Updated: if (this.WriterBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient) { this.entryMetadata.UpdatedString = EpmSyndicationWriter.CreateDateTimeStringValue(propertyValue, this.WriterBehavior); } else { this.entryMetadata.Updated = EpmSyndicationWriter.CreateDateTimeValue(propertyValue, SyndicationItemProperty.Updated, this.WriterBehavior); } break; case SyndicationItemProperty.Published: if (this.WriterBehavior.FormatBehaviorKind == ODataBehaviorKind.WcfDataServicesClient) { this.entryMetadata.PublishedString = EpmSyndicationWriter.CreateDateTimeStringValue(propertyValue, this.WriterBehavior); } else { this.entryMetadata.Published = EpmSyndicationWriter.CreateDateTimeValue(propertyValue, SyndicationItemProperty.Published, this.WriterBehavior); } break; case SyndicationItemProperty.Rights: this.entryMetadata.Rights = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind); break; case SyndicationItemProperty.Summary: this.entryMetadata.Summary = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind); break; case SyndicationItemProperty.Title: this.entryMetadata.Title = EpmSyndicationWriter.CreateAtomTextConstruct(textPropertyValue, epmInfo.Attribute.TargetTextContentKind); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.EpmSyndicationWriter_WriteEntryEpm_ContentTarget)); } } else { this.WriteParentSegment(targetSegment, epmValueCache, entityType); } } return(this.entryMetadata); }