/// <summary> /// Reads an item in the collection. /// </summary> /// <param name="expectedItemType">The expected type of the item to read.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <returns>The value of the collection item that was read; this can be an ODataComplexValue, a primitive value or 'null'.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The start element node of the item in the collection. /// Post-Condition: Any - The next node after the end tag of the item. /// </remarks> internal object ReadCollectionItem(IEdmTypeReference expectedItemType, CollectionWithoutExpectedTypeValidator collectionValidator) { DebugUtils.CheckNoExternalCallers(); this.XmlReader.AssertNotBuffering(); this.AssertXmlCondition(XmlNodeType.Element); // the caller should guarantee that we are reading elements in the OData namespace or the custom namespace specified through the reader settings. Debug.Assert(this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace), "The 'element' node should be in the OData Namespace or in the user specified Namespace"); // make sure that the item is named as 'element'. if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(o.Strings.ODataAtomCollectionDeserializer_WrongCollectionItemElementName(this.XmlReader.LocalName, this.XmlReader.ODataNamespace)); } // We don't support EPM for collections so it is fine to say that EPM is not present object item = this.ReadNonEntityValue(expectedItemType, this.duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /* epmPresent */ false); // read over the end tag of the element or the start tag if the element was empty. this.XmlReader.Read(); this.XmlReader.AssertNotBuffering(); return(item); }
private ODataCollectionValue ReadCollectionValue(IEdmCollectionTypeReference collectionTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { this.IncreaseRecursionDepth(); ODataCollectionValue value2 = new ODataCollectionValue { TypeName = (collectionTypeReference == null) ? payloadTypeName : collectionTypeReference.ODataFullName() }; if (serializationTypeNameAnnotation != null) { value2.SetAnnotation <SerializationTypeNameAnnotation>(serializationTypeNameAnnotation); } base.XmlReader.MoveToElement(); List <object> sourceEnumerable = new List <object>(); if (!base.XmlReader.IsEmptyElement) { base.XmlReader.ReadStartElement(); IEdmTypeReference expectedTypeReference = (collectionTypeReference == null) ? null : collectionTypeReference.ElementType(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); CollectionWithoutExpectedTypeValidator collectionValidator = null; if (collectionTypeReference == null) { string itemTypeNameFromCollection = (payloadTypeName == null) ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); } do { switch (base.XmlReader.NodeType) { case XmlNodeType.Element: if (base.XmlReader.NamespaceEquals(base.XmlReader.ODataNamespace)) { if (!base.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(Microsoft.Data.OData.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(base.XmlReader.LocalName, base.XmlReader.ODataNamespace)); } object item = this.ReadNonEntityValueImplementation(expectedTypeReference, duplicatePropertyNamesChecker, collectionValidator, true, false); base.XmlReader.Read(); ValidationUtils.ValidateCollectionItem(item, false); sourceEnumerable.Add(item); } else { base.XmlReader.Skip(); } break; case XmlNodeType.EndElement: break; default: base.XmlReader.Skip(); break; } }while (base.XmlReader.NodeType != XmlNodeType.EndElement); } value2.Items = new ReadOnlyEnumerable(sourceEnumerable); this.DecreaseRecursionDepth(); return(value2); }
internal void WritePrimitiveValue(object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference) { IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (primitiveTypeReference == null) { throw new ODataException(Microsoft.Data.OData.Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } string collectionItemTypeName = primitiveTypeReference.FullName(); if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(collectionItemTypeName, EdmTypeKind.Primitive); if (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, collectionItemTypeName) == 0) { collectionItemTypeName = null; } } if (expectedTypeReference != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference); } if ((collectionItemTypeName != null) && (collectionItemTypeName != "Edm.String")) { this.WritePropertyTypeAttribute(collectionItemTypeName); } AtomValueUtils.WritePrimitiveValue(base.XmlWriter, value); }
internal void WritePrimitiveValue(object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference) { IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (collectionValidator != null) { if (primitiveTypeReference == null) { throw new ODataException(Microsoft.Data.OData.Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive); } if (expectedTypeReference != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference); } if ((primitiveTypeReference != null) && primitiveTypeReference.IsSpatial()) { string typeName = primitiveTypeReference.FullName(); PrimitiveConverter.Instance.WriteJson(value, base.JsonWriter, typeName, base.Version); } else { base.JsonWriter.WritePrimitiveValue(value, base.Version); } }
/// <summary> /// Asynchronously reads an item in the collection. /// </summary> /// <param name="expectedItemTypeReference">The expected type of the item to read.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <returns> /// A task that represents the asynchronous read operation. /// The value of the TResult parameter contains the value of the collection item that was read; this can be a primitive value or 'null'. /// </returns> /// <remarks> /// Pre-Condition: The first node of the item in the collection /// NOTE: this method will throw if the node is not /// JsonNodeType.PrimitiveValue: for a primitive item /// Post-Condition: The reader is positioned on the first node of the next item or an EndArray node if there are no more items in the collection /// </remarks> internal async Task <object> ReadCollectionItemAsync( IEdmTypeReference expectedItemTypeReference, CollectionWithoutExpectedTypeValidator collectionValidator) { Debug.Assert( expectedItemTypeReference == null || expectedItemTypeReference.IsODataPrimitiveTypeKind() || expectedItemTypeReference.IsODataEnumTypeKind() || expectedItemTypeReference.IsODataTypeDefinitionTypeKind(), "If an expected type is specified, it must be a primitive, enum type or type definition."); this.JsonReader.AssertNotBuffering(); object item = await this.ReadNonEntityValueAsync( payloadTypeName : null, expectedValueTypeReference : expectedItemTypeReference, propertyAndAnnotationCollector : this.propertyAndAnnotationCollector, collectionValidator : collectionValidator, validateNullValue : true, isTopLevelPropertyValue : false, insideResourceValue : false, propertyName : null).ConfigureAwait(false); this.JsonReader.AssertNotBuffering(); return(item); }
internal void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType) { this.IncreaseRecursionDepth(); base.JsonWriter.StartObjectScope(); string typeName = collectionValue.TypeName; IEdmCollectionTypeReference type = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string itemTypeNameFromCollection = null; if (typeName != null) { itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation annotation = collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (typeName != null) { base.JsonWriter.WriteName("__metadata"); base.JsonWriter.StartObjectScope(); base.JsonWriter.WriteName("type"); base.JsonWriter.WriteValue(typeName); base.JsonWriter.EndObjectScope(); } base.JsonWriter.WriteDataArrayName(); base.JsonWriter.StartArrayScope(); IEnumerable items = collectionValue.Items; if (items != null) { IEdmTypeReference propertyTypeReference = (type == null) ? null : type.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object obj2 in items) { ValidationUtils.ValidateCollectionItem(obj2, false); ODataComplexValue complexValue = obj2 as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue(complexValue, propertyTypeReference, false, duplicatePropertyNamesChecker, collectionValidator); duplicatePropertyNamesChecker.Clear(); } else { this.WritePrimitiveValue(obj2, collectionValidator, propertyTypeReference); } } } base.JsonWriter.EndArrayScope(); base.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }
internal object ReadCollectionItem(IEdmTypeReference expectedItemType, CollectionWithoutExpectedTypeValidator collectionValidator) { if (!base.XmlReader.LocalNameEquals(base.ODataCollectionItemElementName)) { throw new ODataException(Microsoft.Data.OData.Strings.ODataAtomCollectionDeserializer_WrongCollectionItemElementName(base.XmlReader.LocalName, base.XmlReader.ODataNamespace)); } object obj2 = base.ReadNonEntityValue(expectedItemType, this.duplicatePropertyNamesChecker, collectionValidator, true, false); base.XmlReader.Read(); return(obj2); }
private void WriteCollectionValue(ODataCollectionValue collectionValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, bool isWritingCollection) { this.IncreaseRecursionDepth(); string typeName = collectionValue.TypeName; IEdmCollectionTypeReference type = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(base.Model, propertyTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string itemTypeNameFromCollection = null; if (typeName != null) { itemTypeNameFromCollection = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation annotation = collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (typeName != null) { this.WritePropertyTypeAttribute(typeName); } IEdmTypeReference metadataTypeReference = (type == null) ? null : type.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeNameFromCollection); IEnumerable items = collectionValue.Items; if (items != null) { DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object obj2 in items) { ValidationUtils.ValidateCollectionItem(obj2, false); base.XmlWriter.WriteStartElement("d", "element", base.MessageWriterSettings.WriterBehavior.ODataNamespace); ODataComplexValue complexValue = obj2 as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue(complexValue, metadataTypeReference, false, isWritingCollection, null, null, duplicatePropertyNamesChecker, collectionValidator, null, null, null); duplicatePropertyNamesChecker.Clear(); } else { this.WritePrimitiveValue(obj2, collectionValidator, metadataTypeReference); } base.XmlWriter.WriteEndElement(); } } this.DecreaseRecursionDepth(); }
/// <summary> /// Writes out the value of a complex property. /// </summary> /// <param name="complexValue">The complex value to write.</param> /// <param name="propertyTypeReference">The metadata type for the complex value.</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="collectionValidator">The collection validator instance to validate the type names and type kinds of collection items; null if no validation is needed.</param> /// <remarks>The current recursion depth should be a value, measured by the number of complex and collection values between /// this complex value and the top-level payload, not including this one.</remarks> internal void WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire complex instance this.JsonWriter.StartObjectScope(); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } // resolve the type name to the type; if no type name is specified we will use the type inferred from metadata IEdmComplexTypeReference complexValueTypeReference = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, complexValue, isOpenPropertyType).AsComplexOrNull(); string collectionItemTypeName; typeName = this.VerboseJsonOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexValueTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); // Write the "__metadata" : { "type": "typename" } // But only if we actually have a typename to write, otherwise we need the __metadata to be omitted entirely if (typeName != null) { ODataJsonWriterUtils.WriteMetadataWithTypeName(this.JsonWriter, typeName); } // Write the properties of the complex value as usual. Note we do not allow complex types to contain named stream properties. this.WriteProperties( complexValueTypeReference == null ? null : complexValueTypeReference.ComplexDefinition(), complexValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null /*projectedProperties */); // End the object scope which represents the complex instance this.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }
/// <summary> /// Reads the primitive, complex or collection value. /// </summary> /// <param name="expectedValueTypeReference">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> /// <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> internal object ReadNonEntityValue( IEdmTypeReference expectedValueTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue) { this.AssertRecursionDepthIsZero(); object nonEntityValue = this.ReadNonEntityValueImplementation( expectedValueTypeReference, duplicatePropertyNamesChecker, collectionValidator, validateNullValue, /*propertyName*/ null); this.AssertRecursionDepthIsZero(); return(nonEntityValue); }
/// <summary> /// Reads the primitive, complex or collection value. /// </summary> /// <param name="expectedValueTypeReference">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; otherwise false.</param> /// <param name="epmPresent">Whether any EPM mappings exist.</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> internal object ReadNonEntityValue( IEdmTypeReference expectedValueTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, bool epmPresent) { DebugUtils.CheckNoExternalCallers(); this.AssertRecursionDepthIsZero(); object nonEntityValue = this.ReadNonEntityValueImplementation( expectedValueTypeReference, duplicatePropertyNamesChecker, collectionValidator, validateNullValue, epmPresent); this.AssertRecursionDepthIsZero(); return(nonEntityValue); }
/// <summary> /// Writes a primitive value. /// </summary> /// <param name="value">The value to write.</param> /// <param name="collectionValidator">The collection validator instance.</param> /// <param name="expectedTypeReference">The expected type of the primitive value.</param> internal void WritePrimitiveValue( object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(value != null, "value != null"); IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (primitiveTypeReference == null) { throw new ODataException(o.Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } string typeName = primitiveTypeReference.FullName(); if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Primitive); if (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0) { typeName = null; } } if (expectedTypeReference != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference); } if (typeName != null && typeName != Metadata.EdmConstants.EdmStringTypeName) { this.WritePropertyTypeAttribute(typeName); } AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value); }
/// <summary> /// Writes an enumeration value. /// </summary> /// <param name="value">The value to write.</param> /// <param name="collectionValidator">The collection validator instance.</param> /// <param name="expectedTypeReference">The expected type of the enumeration value.</param> /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this enumeration value. The annotation value will override whatever type name is being written.</param> internal void WriteEnumValue( ODataEnumValue value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference, SerializationTypeNameAnnotation typeNameAnnotation) { Debug.Assert(value != null, "value != null"); // write type name without validation: string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, expectedTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); if (typeName != null) { Debug.Assert(typeName != EdmConstants.EdmStringTypeName, "Enum typeName != EdmConstants.StringTypeName"); this.WritePropertyTypeAttribute(typeName); } // write string value without validation: AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value.Value); }
/// <summary> /// Writes a primitive value. /// </summary> /// <param name="value">The value to write.</param> /// <param name="collectionValidator">The collection validator instance.</param> /// <param name="expectedTypeReference">The expected type of the primitive value.</param> /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this primitive value. The annotation value will override whatever type name is being written.</param> internal void WritePrimitiveValue( object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference, SerializationTypeNameAnnotation typeNameAnnotation) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(value != null, "value != null"); IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (primitiveTypeReference == null) { throw new ODataException(ODataErrorStrings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive); } if (expectedTypeReference != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference); } string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, primitiveTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); if (typeName != null && typeName != EdmConstants.EdmStringTypeName) { this.WritePropertyTypeAttribute(typeName); } AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value); }
/// <summary> /// Writes a primitive value. /// Uses a registered primitive type converter to write the value if one is registered for the type, otherwise directly writes the value. /// </summary> /// <param name="value">The value to write.</param> /// <param name="collectionValidator">The collection validator instance.</param> /// <param name="expectedTypeReference">The expected type reference of the primitive value.</param> internal void WritePrimitiveValue( object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(value != null, "value != null"); IEdmPrimitiveTypeReference actualTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (collectionValidator != null) { if (actualTypeReference == null) { throw new ODataException(o.Strings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } collectionValidator.ValidateCollectionItem(actualTypeReference.FullName(), EdmTypeKind.Primitive); } if (expectedTypeReference != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(value, actualTypeReference, expectedTypeReference); } if (actualTypeReference != null && actualTypeReference.IsSpatial()) { // For spatial types, we will always write the type name. This is consistent with complex types. string typeName = actualTypeReference.FullName(); PrimitiveConverter.Instance.WriteJson(value, this.JsonWriter, typeName, this.Version); } else { this.JsonWriter.WritePrimitiveValue(value, this.Version); } }
internal bool WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType).AsComplexOrNull(); string collectionItemTypeName; typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); Action beforeValueCallbackWithTypeName = beforeValueAction; if (typeName != null) { // The beforeValueAction (if specified) will write the actual property element start. // So if we are to write the type attribute, we must postpone that after the start element was written. // And so we chain the existing action with our type attribute writing and use that // as the before action instead. if (beforeValueAction != null) { beforeValueCallbackWithTypeName = () => { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } else { this.WritePropertyTypeAttribute(typeName); } } // NOTE: see the comments on ODataWriterBehavior.UseV1ProviderBehavior for more information // NOTE: We have to check for ProjectedPropertiesAnnotation.Empty here to avoid filling the cache for // complex values we are writing only to ensure we don't have nested EPM-mapped null values // that will end up in the content eventually. if (this.MessageWriterSettings.WriterBehavior != null && this.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesInstance)) { IEdmComplexType complexType = (IEdmComplexType)complexTypeReference.Definition; CachedPrimitiveKeepInContentAnnotation keepInContentCache = this.Model.EpmCachedKeepPrimitiveInContent(complexType); if (keepInContentCache == null) { // we are about to write the first value of the given type; compute the keep-in-content information for the primitive properties of this type. List <string> keepInContentPrimitiveProperties = null; // initialize the cache with all primitive properties foreach (IEdmProperty edmProperty in complexType.Properties().Where(p => p.Type.IsODataPrimitiveTypeKind())) { // figure out the keep-in-content value EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, edmProperty.Name); if (entityPropertyMapping != null && entityPropertyMapping.KeepInContent) { if (keepInContentPrimitiveProperties == null) { keepInContentPrimitiveProperties = new List <string>(); } keepInContentPrimitiveProperties.Add(edmProperty.Name); } } this.Model.SetAnnotationValue <CachedPrimitiveKeepInContentAnnotation>(complexType, new CachedPrimitiveKeepInContentAnnotation(keepInContentPrimitiveProperties)); } } bool propertyWritten = this.WriteProperties( complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforeValueCallbackWithTypeName, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties); this.DecreaseRecursionDepth(); return(propertyWritten); }
/// <summary> /// Reads an item in the collection. /// </summary> /// <param name="expectedItemTypeReference">The expected type of the item to read.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <returns>The value of the collection item that was read; this can be a primitive value or 'null'.</returns> /// <remarks> /// Pre-Condition: The first node of the item in the collection /// NOTE: this method will throw if the node is not /// JsonNodeType.PrimitiveValue: for a primitive item /// Post-Condition: The reader is positioned on the first node of the next item or an EndArray node if there are no more items in the collection /// </remarks> internal object ReadCollectionItem(IEdmTypeReference expectedItemTypeReference, CollectionWithoutExpectedTypeValidator collectionValidator) { Debug.Assert( expectedItemTypeReference == null || expectedItemTypeReference.IsODataPrimitiveTypeKind() || expectedItemTypeReference.IsODataEnumTypeKind() || expectedItemTypeReference.IsODataTypeDefinitionTypeKind(), "If an expected type is specified, it must be a primitive, enum type or type definition."); this.JsonReader.AssertNotBuffering(); object item = this.ReadNonEntityValue( /*payloadTypeName*/ null, expectedItemTypeReference, this.propertyAndAnnotationCollector, collectionValidator, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideResourceValue*/ false, /*propertyName*/ null); this.JsonReader.AssertNotBuffering(); return(item); }
/// <summary> /// Reads a primitive value, complex value or collection. /// </summary> /// <param name="payloadTypeName">The type name read from the payload as a property annotation, or null if none is available.</param> /// <param name="expectedValueTypeReference">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="isTopLevelPropertyValue">true if we are reading a top-level property value; otherwise false.</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="propertyName">The name of the property whose value is being read, if applicable (used for error reporting).</param> /// <param name="isDynamicProperty">Indicates whether the property is dynamic or unknown.</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> internal object ReadNonEntityValue( string payloadTypeName, IEdmTypeReference expectedValueTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, bool isTopLevelPropertyValue, bool insideComplexValue, string propertyName, bool? isDynamicProperty = null) { this.AssertRecursionDepthIsZero(); object nonEntityValue = this.ReadNonEntityValueImplementation( payloadTypeName, expectedValueTypeReference, duplicatePropertyNamesChecker, collectionValidator, validateNullValue, isTopLevelPropertyValue, insideComplexValue, propertyName, isDynamicProperty); this.AssertRecursionDepthIsZero(); return nonEntityValue; }
/// <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="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, 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.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.Enum: Debug.Assert(targetTypeReference != null && targetTypeReference.IsODataEnumTypeKind(), "Expected an OData Enum type."); result = this.ReadEnumValue(targetTypeReference.AsEnum()); break; 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); 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> /// Read a collection from the reader. /// </summary> /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param> /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param> /// <returns>The value read from the payload.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - the element to read the value for. /// XmlNodeType.Attribute - an attribute on the element to read the value for. /// Post-Condition: XmlNodeType.Element - the element was empty. /// XmlNodeType.EndElement - the element had some value. /// /// Note that this method will not read null values, those should be handled by the caller already. /// </remarks> private ODataCollectionValue ReadCollectionValue( IEdmCollectionTypeReference collectionTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute); Debug.Assert( collectionTypeReference == null || collectionTypeReference.IsNonEntityCollectionType(), "If the metadata is specified it must denote a collection for this method to work."); this.IncreaseRecursionDepth(); ODataCollectionValue collectionValue = new ODataCollectionValue(); // If we have a metadata type for the collection, use that type name // otherwise use the type name from the payload (if there was any). collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName(); if (serializationTypeNameAnnotation != null) { collectionValue.SetAnnotation(serializationTypeNameAnnotation); } // Move to the element (so that if we were on an attribute we can test the element for being empty) this.XmlReader.MoveToElement(); List<object> items = new List<object>(); // Empty collections are valid - they have no items if (!this.XmlReader.IsEmptyElement) { // Read over the collection element to its first child node (or end-element) this.XmlReader.ReadStartElement(); // If we don't have metadata (that is collectionType is null) we parse the type name // and extract the item type name from it. Then we create a CollectionWithoutExpectedTypeValidator // instance and use it to ensure that all item types read for the collection value are consistent with // the item type derived from the collection value's type name. // Note that if an item does not specify a type name but the collection value does, the item will be // assigned the item type name computed from the collection value's type. // Note that JSON doesn't have this problem since in JSON we always have metadata and thus the collectionType // is never null by the time we get to a similar place in the code. IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); CollectionWithoutExpectedTypeValidator collectionValidator = null; if (collectionTypeReference == null) { // Parse the type name from the payload (if any), extract the item type name and construct a collection validator string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName); } do { switch (this.XmlReader.NodeType) { case XmlNodeType.Element: if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace)) { if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { this.XmlReader.Skip(); } else { object itemValue = this.ReadNonEntityValueImplementation( itemTypeReference, duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /*propertyName*/ null); // read over the end tag of the element or the start tag if the element was empty. this.XmlReader.Read(); // Validate the item (for example that it's not null) ValidationUtils.ValidateCollectionItem(itemValue, itemTypeReference.IsNullable()); // Note that the ReadNonEntityValue already validated that the actual type of the value matches // the expected type (the itemType). items.Add(itemValue); } } else if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { throw new ODataException(ODataErrorStrings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataMetadataNamespace)); } else { this.XmlReader.Skip(); } break; case XmlNodeType.EndElement: // End of the collection. break; default: // Non-element so for example a text node, just ignore this.XmlReader.Skip(); break; } } while (this.XmlReader.NodeType != XmlNodeType.EndElement); } collectionValue.Items = new ReadOnlyEnumerable(items); this.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values."); this.DecreaseRecursionDepth(); return collectionValue; }
internal bool WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, ProjectedPropertiesAnnotation projectedProperties) { Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeForComplexValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType, this.WriterValidator).AsComplexOrNull(); string collectionItemTypeName; typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation<SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); Action beforeValueCallbackWithTypeName = beforeValueAction; if (typeName != null) { // The beforeValueAction (if specified) will write the actual property element start. // So if we are to write the type attribute, we must postpone that after the start element was written. // And so we chain the existing action with our type attribute writing and use that // as the before action instead. if (beforeValueAction != null) { beforeValueCallbackWithTypeName = () => { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } else { this.WritePropertyTypeAttribute(typeName); } } bool propertyWritten = this.WriteProperties( complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(), complexValue.Properties, isWritingCollection, beforeValueCallbackWithTypeName, afterValueAction, duplicatePropertyNamesChecker, projectedProperties); this.DecreaseRecursionDepth(); return propertyWritten; }
/// <summary> /// Reads an item in the collection. /// </summary> /// <param name="expectedItemTypeReference">The expected type of the item to read.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <returns>The value of the collection item that was read; this can be an ODataComplexValue, a primitive value or 'null'.</returns> /// <remarks> /// Pre-Condition: The first node of the item in the collection /// NOTE: this method will throw if the node is not /// JsonNodeType.StartObject: for a complex item /// JsonNodeType.PrimitiveValue: for a primitive item /// Post-Condition: The reader is positioned on the first node of the next item or an EndArray node if there are no more items in the collection /// </remarks> internal object ReadCollectionItem(IEdmTypeReference expectedItemTypeReference, CollectionWithoutExpectedTypeValidator collectionValidator) { Debug.Assert( expectedItemTypeReference == null || expectedItemTypeReference.IsODataPrimitiveTypeKind() || expectedItemTypeReference.IsODataComplexTypeKind() || expectedItemTypeReference.IsODataEnumTypeKind() || expectedItemTypeReference.IsODataTypeDefinitionTypeKind(), "If an expected type is specified, it must be a primitive, complex type, enum type or type definition."); this.JsonReader.AssertNotBuffering(); object item = this.ReadNonEntityValue( /*payloadTypeName*/ null, expectedItemTypeReference, this.duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, /*propertyName*/ null); this.JsonReader.AssertNotBuffering(); return item; }
/// <summary> /// Write the items of a collection in ATOM format. /// </summary> /// <param name="collectionValue">The collection value to write.</param> /// <param name="propertyTypeReference">The type reference of the collection value (or null if not metadata is available).</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param> private void WriteCollectionValue( ODataCollectionValue collectionValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, bool isWritingCollection) { Debug.Assert(collectionValue != null, "collectionValue != null"); this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeNameForValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType); string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName); if (typeName != null) { this.WritePropertyTypeAttribute(typeName); } IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName); IEnumerable items = collectionValue.Items; if (items != null) { DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object item in items) { ValidationUtils.ValidateCollectionItem(item, false /* isStreamable */); this.XmlWriter.WriteStartElement(AtomConstants.ODataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, this.MessageWriterSettings.WriterBehavior.ODataNamespace); ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue( complexValue, expectedItemTypeReference, false, isWritingCollection, null /* beforeValueAction */, null /* afterValueAction */, duplicatePropertyNamesChecker, collectionValidator, null /* epmValueCache */, null /* epmSourcePathSegment */, null /* projectedProperties */); duplicatePropertyNamesChecker.Clear(); } else { Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)"); Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)"); // Note: Currently there is no way for a user to control primitive type information when the primitive values are part of a collection. this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null); } this.XmlWriter.WriteEndElement(); } } this.DecreaseRecursionDepth(); }
/// <summary> /// Writes a primitive value. /// </summary> /// <param name="value">The value to write.</param> /// <param name="collectionValidator">The collection validator instance.</param> /// <param name="expectedTypeReference">The expected type of the primitive value.</param> /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this primitive value. The annotation value will override whatever type name is being written.</param> internal void WritePrimitiveValue( object value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference, SerializationTypeNameAnnotation typeNameAnnotation) { Debug.Assert(value != null, "value != null"); IEdmPrimitiveTypeReference primitiveTypeReference = EdmLibraryExtensions.GetPrimitiveTypeReference(value.GetType()); if (primitiveTypeReference == null) { throw new ODataException(ODataErrorStrings.ValidationUtils_UnsupportedPrimitiveType(value.GetType().FullName)); } if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(primitiveTypeReference.FullName(), EdmTypeKind.Primitive); } if (expectedTypeReference != null) { ValidationUtils.ValidateIsExpectedPrimitiveType(value, primitiveTypeReference, expectedTypeReference); } string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, primitiveTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); if (typeName != null && typeName != EdmConstants.EdmStringTypeName) { this.WritePropertyTypeAttribute(typeName); } AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value); }
/// <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; otherwise false.</param> /// <param name="epmPresent">Whether any EPM mappings exist.</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) { 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); } 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(o.Strings.General_InternalError(InternalErrorCodes.ODataAtomPropertyAndValueDeserializer_ReadNonEntityValue)); } } this.AssertXmlCondition(true, XmlNodeType.EndElement); this.XmlReader.AssertNotBuffering(); return(result); }
private object ReadNonEntityValueImplementation( string payloadTypeName, IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue, bool isTopLevelPropertyValue, bool insideComplexValue, string propertyName, bool? isDynamicProperty = null) { Debug.Assert( this.JsonReader.NodeType == JsonNodeType.PrimitiveValue || this.JsonReader.NodeType == JsonNodeType.StartObject || this.JsonReader.NodeType == JsonNodeType.StartArray || (insideComplexValue && (this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject)), "Pre-Condition: expected JsonNodeType.PrimitiveValue or JsonNodeType.StartObject or JsonNodeType.StartArray or JsonNodeTypeProperty (when inside complex value)."); 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."); bool valueIsJsonObject = this.JsonReader.NodeType == JsonNodeType.StartObject; bool payloadTypeNameFromPropertyAnnotation = !insideComplexValue && payloadTypeName != null; bool typeNameFoundInPayload = false; if (valueIsJsonObject || insideComplexValue) { // If we have an object value initialize the duplicate property names checker if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } else { duplicatePropertyNamesChecker.Clear(); } // Read the payload type name if (!insideComplexValue) { string typeName; typeNameFoundInPayload = this.TryReadPayloadTypeFromObject( duplicatePropertyNamesChecker, insideComplexValue, out typeName); if (typeNameFoundInPayload) { payloadTypeName = typeName; } } } SerializationTypeNameAnnotation serializationTypeNameAnnotation; EdmTypeKind targetTypeKind; IEdmTypeReference targetTypeReference = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType( EdmTypeKind.None, /*defaultPrimitivePayloadType*/ null, expectedTypeReference, payloadTypeName, this.Model, this.MessageReaderSettings, this.GetNonEntityValueKind, out targetTypeKind, out serializationTypeNameAnnotation); object result; // Try to read a null value if (ODataJsonReaderCoreUtils.TryReadNullValue(this.JsonReader, this.JsonLightInputContext, targetTypeReference, validateNullValue, propertyName, isDynamicProperty)) { if (isTopLevelPropertyValue) { // For a top-level property value a special null marker object has to be used to indicate a null value. // If we find a null value for a property at the top-level, it is an invalid payload throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_TopLevelPropertyWithPrimitiveNullValue(ODataAnnotationNames.ODataNull, JsonLightConstants.ODataNullAnnotationTrueValue)); } else if (validateNullValue && (targetTypeReference != null) && (!targetTypeReference.IsNullable)) { // For dynamic collection property, we should allow null value to be assigned to it. if (targetTypeKind != EdmTypeKind.Collection || isDynamicProperty != true) { // A null value was found for the property named '{0}', which has the expected type '{1}[Nullable=False]'. The expected type '{1}[Nullable=False]' does not allow null values. throw new ODataException(ODataErrorStrings.ReaderValidationUtils_NullNamedValueForNonNullableType(propertyName, targetTypeReference.FullName())); } } result = null; } else { Debug.Assert( !valueIsJsonObject || this.JsonReader.NodeType == JsonNodeType.Property || this.JsonReader.NodeType == JsonNodeType.EndObject, "If the value was an object the reader must be on either property or end object."); switch (targetTypeKind) { case EdmTypeKind.Primitive: Debug.Assert(targetTypeReference == null || targetTypeReference.IsODataPrimitiveTypeKind(), "Expected an OData primitive type."); IEdmPrimitiveTypeReference primitiveTargetTypeReference = targetTypeReference == null ? null : targetTypeReference.AsPrimitive(); // If we found an odata.type annotation inside a primitive value, we have to fail; type annotations // for primitive values are property annotations, not instance annotations inside the value. if (typeNameFoundInPayload) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_ODataTypeAnnotationInPrimitiveValue(ODataAnnotationNames.ODataType)); } result = this.ReadPrimitiveValue( valueIsJsonObject, primitiveTargetTypeReference, validateNullValue, propertyName); break; case EdmTypeKind.Enum: Debug.Assert(targetTypeReference == null || targetTypeReference.IsODataEnumTypeKind(), "Expected an OData enum type."); IEdmEnumTypeReference enumTargetTypeReference = targetTypeReference == null ? null : targetTypeReference.AsEnum(); result = this.ReadEnumValue( valueIsJsonObject, enumTargetTypeReference, validateNullValue, propertyName); break; case EdmTypeKind.Complex: Debug.Assert(targetTypeReference == null || targetTypeReference.IsComplex(), "Expected null or a complex type."); if (payloadTypeNameFromPropertyAnnotation) { // We already have type name specified as annotation on the parent property. // OData type property annotation on a complex value - fail. throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_ComplexValueWithPropertyTypeAnnotation(ODataAnnotationNames.ODataType)); } if (!valueIsJsonObject && !insideComplexValue) { this.JsonReader.ReadStartObject(); } result = this.ReadComplexValue( targetTypeReference == null ? null : targetTypeReference.AsComplex(), payloadTypeName, serializationTypeNameAnnotation, duplicatePropertyNamesChecker); break; case EdmTypeKind.Collection: IEdmCollectionTypeReference collectionTypeReference = ValidationUtils.ValidateCollectionType(targetTypeReference); if (valueIsJsonObject) { // We manually throw JSON exception here to get a nicer error message (we expect array value and got object). // Otherwise the ReadCollectionValue would fail with something like "expected array value but found property/end object" which is rather confusing. throw new ODataException(ODataErrorStrings.JsonReaderExtensions_UnexpectedNodeDetectedWithPropertyName(JsonNodeType.StartArray, JsonNodeType.StartObject, propertyName)); } result = this.ReadCollectionValue( collectionTypeReference, payloadTypeName, serializationTypeNameAnnotation); break; case EdmTypeKind.TypeDefinition: result = this.ReadTypeDefinitionValue( valueIsJsonObject, expectedTypeReference.AsTypeDefinition(), validateNullValue, propertyName); break; default: throw new ODataException(ODataErrorStrings.General_InternalError(InternalErrorCodes.ODataJsonLightPropertyAndValueDeserializer_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 = ODataJsonLightReaderUtils.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); } } return result; }
/// <summary> /// Write the items of a collection in ATOM format. /// </summary> /// <param name="collectionValue">The collection value to write.</param> /// <param name="propertyTypeReference">The type reference of the collection value (or null if not metadata is available).</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param> private void WriteCollectionValue( ODataCollectionValue collectionValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, bool isWritingCollection) { Debug.Assert(collectionValue != null, "collectionValue != null"); this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator); string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName); if (typeName != null) { this.WritePropertyTypeAttribute(typeName); } // COMPAT 4: ATOM and JSON format for collections - The collection format is not formally specified yet, since it's a new feature // If the official format deviates from the current WCFDS behavior we would have to introduce a back compat mode here as well. IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName); IEnumerable items = collectionValue.Items; if (items != null) { DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object item in items) { ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable()); this.XmlWriter.WriteStartElement(AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, AtomConstants.ODataMetadataNamespace); ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue( complexValue, expectedItemTypeReference, false, isWritingCollection, null /* beforeValueAction */, null /* afterValueAction */, duplicatePropertyNamesChecker, collectionValidator, null /* projectedProperties */); duplicatePropertyNamesChecker.Clear(); } else { Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)"); Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)"); ODataEnumValue enumValue = item as ODataEnumValue; if (enumValue != null) { // Note: Currently there is no way for a user to control enum type's serializationTypeNameAnnotation information when the enum values are part of a collection. this.WriteEnumValue(enumValue, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null); } else { // Note: Currently there is no way for a user to control primitive type information when the primitive values are part of a collection. if (item != null) { this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null); } else { this.WriteNullCollectionElementValue(expectedItemTypeReference); } } } this.XmlWriter.WriteEndElement(); } } this.DecreaseRecursionDepth(); }
internal bool WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, ProjectedPropertiesAnnotation projectedProperties) { Debug.Assert(complexValue != null, "complexValue != null"); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmComplexTypeReference complexTypeReference = TypeNameOracle.ResolveAndValidateTypeForComplexValue(this.Model, metadataTypeReference, complexValue, isOpenPropertyType, this.WriterValidator).AsComplexOrNull(); string collectionItemTypeName; typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(complexValue, complexTypeReference, complexValue.GetAnnotation <SerializationTypeNameAnnotation>(), collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); Action beforeValueCallbackWithTypeName = beforeValueAction; if (typeName != null) { // The beforeValueAction (if specified) will write the actual property element start. // So if we are to write the type attribute, we must postpone that after the start element was written. // And so we chain the existing action with our type attribute writing and use that // as the before action instead. if (beforeValueAction != null) { beforeValueCallbackWithTypeName = () => { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } else { this.WritePropertyTypeAttribute(typeName); } } bool propertyWritten = this.WriteProperties( complexTypeReference == null ? null : complexTypeReference.ComplexDefinition(), complexValue.Properties, isWritingCollection, beforeValueCallbackWithTypeName, afterValueAction, duplicatePropertyNamesChecker, projectedProperties); this.DecreaseRecursionDepth(); return(propertyWritten); }
private object ReadNonEntityValueImplementation(IEdmTypeReference expectedTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue) { object obj2; SerializationTypeNameAnnotation annotation; EdmTypeKind kind; JsonNodeType nodeType = base.JsonReader.NodeType; if (nodeType == JsonNodeType.StartArray) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_CannotReadPropertyValue(nodeType)); } if (this.TryReadNullValue(expectedTypeReference, validateNullValue)) { return(null); } string payloadTypeName = this.FindTypeNameInPayload(); IEdmTypeReference type = ReaderValidationUtils.ResolvePayloadTypeNameAndComputeTargetType(EdmTypeKind.None, null, expectedTypeReference, payloadTypeName, base.Model, base.MessageReaderSettings, base.Version, new Func <EdmTypeKind>(this.GetNonEntityValueKind), out kind, out annotation); switch (kind) { case EdmTypeKind.Primitive: { IEdmPrimitiveTypeReference reference2 = (type == null) ? null : type.AsPrimitive(); if ((payloadTypeName != null) && !reference2.IsSpatial()) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_InvalidPrimitiveTypeName(payloadTypeName)); } obj2 = this.ReadPrimitiveValueImplementation(reference2, validateNullValue); break; } case EdmTypeKind.Complex: obj2 = this.ReadComplexValueImplementation((type == null) ? null : type.AsComplex(), payloadTypeName, annotation, duplicatePropertyNamesChecker); break; case EdmTypeKind.Collection: { IEdmCollectionTypeReference collectionValueTypeReference = ValidationUtils.ValidateCollectionType(type); obj2 = this.ReadCollectionValueImplementation(collectionValueTypeReference, payloadTypeName, annotation); break; } default: throw new ODataException(Microsoft.Data.OData.Strings.General_InternalError(InternalErrorCodes.ODataJsonPropertyAndValueDeserializer_ReadPropertyValue)); } if (collectionValidator != null) { string collectionItemTypeName = ODataJsonReaderUtils.GetPayloadTypeName(obj2); collectionValidator.ValidateCollectionItem(collectionItemTypeName, kind); } return(obj2); }
/// <summary> /// Reads an item in the collection. /// </summary> /// <param name="expectedItemType">The expected type of the item to read.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <returns>The value of the collection item that was read; this can be an ODataComplexValue, a primitive value or 'null'.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - The start element node of the item in the collection. /// Post-Condition: Any - The next node after the end tag of the item. /// </remarks> internal object ReadCollectionItem(IEdmTypeReference expectedItemType, CollectionWithoutExpectedTypeValidator collectionValidator) { this.XmlReader.AssertNotBuffering(); this.AssertXmlCondition(XmlNodeType.Element); // the caller should guarantee that we are reading elements in the OData namespace or the custom namespace specified through the reader settings. Debug.Assert(this.XmlReader.NamespaceEquals(this.XmlReader.ODataMetadataNamespace), "The 'element' node should be in the OData Metadata Namespace or in the user specified Metadata Namespace"); // make sure that the item is named as 'element'. if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(ODataErrorStrings.ODataAtomCollectionDeserializer_WrongCollectionItemElementName(this.XmlReader.LocalName, this.XmlReader.ODataNamespace)); } object item = this.ReadNonEntityValue(expectedItemType, this.duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true); // read over the end tag of the element or the start tag if the element was empty. this.XmlReader.Read(); this.XmlReader.AssertNotBuffering(); return item; }
/// <summary> /// Writes out the value of a collection property. /// </summary> /// <param name="collectionValue">The collection value to write.</param> /// <param name="metadataTypeReference">The metadata type reference for the collection.</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <remarks>The current recursion depth is measured by the number of complex and collection values between /// this one and the top-level payload, not including this one.</remarks> internal void WriteCollectionValue( ODataCollectionValue collectionValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(collectionValue != null, "collectionValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire CollectionValue instance this.JsonWriter.StartObjectScope(); // "__metadata": { "type": "typename" } // If the CollectionValue has type information write out the metadata and the type in it. string typeName = collectionValue.TypeName; // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)WriterValidationUtils.ResolveTypeNameForWriting(this.Model, metadataTypeReference, ref typeName, EdmTypeKind.Collection, isOpenPropertyType); string collectionItemTypeName = null; if (typeName != null) { collectionItemTypeName = ValidationUtils.ValidateCollectionTypeName(typeName); } SerializationTypeNameAnnotation serializationTypeNameAnnotation = collectionValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { typeName = serializationTypeNameAnnotation.TypeName; } if (typeName != null) { // Write the __metadata object this.JsonWriter.WriteName(JsonConstants.ODataMetadataName); this.JsonWriter.StartObjectScope(); // "type": "typename" this.JsonWriter.WriteName(JsonConstants.ODataMetadataTypeName); this.JsonWriter.WriteValue(typeName); // End the __metadata this.JsonWriter.EndObjectScope(); } // "results": [ // This represents the array of items in the CollectionValue this.JsonWriter.WriteDataArrayName(); this.JsonWriter.StartArrayScope(); // Iterate through the CollectionValue items and write them out (treat null Items as an empty enumeration) IEnumerable items = collectionValue.Items; if (items != null) { IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object item in items) { ValidationUtils.ValidateCollectionItem(item, false /* isStreamable */); ODataComplexValue itemAsComplexValue = item as ODataComplexValue; if (itemAsComplexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue( itemAsComplexValue, expectedItemTypeReference, false, duplicatePropertyNamesChecker, collectionValidator); duplicatePropertyNamesChecker.Clear(); } else { Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)"); Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)"); this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference); } } } // End the array scope which holds the items this.JsonWriter.EndArrayScope(); // End the object scope which holds the entire collection this.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }
/// <summary> /// Writes an enumeration value. /// </summary> /// <param name="value">The value to write.</param> /// <param name="collectionValidator">The collection validator instance.</param> /// <param name="expectedTypeReference">The expected type of the enumeration value.</param> /// <param name="typeNameAnnotation">The optional type name annotation provided by the user on the OM for this enumeration value. The annotation value will override whatever type name is being written.</param> internal void WriteEnumValue( ODataEnumValue value, CollectionWithoutExpectedTypeValidator collectionValidator, IEdmTypeReference expectedTypeReference, SerializationTypeNameAnnotation typeNameAnnotation) { Debug.Assert(value != null, "value != null"); // write type name without validation: // TODO: consider adding a overloaded GetValueTypeNameForWriting() without out parameter. string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(value, expectedTypeReference, typeNameAnnotation, collectionValidator, out collectionItemTypeName); Debug.Assert(collectionItemTypeName == null, "collectionItemTypeName == null"); if (typeName != null) { Debug.Assert(typeName != EdmConstants.EdmStringTypeName, "Enum typeName != EdmConstants.StringTypeName"); this.WritePropertyTypeAttribute(typeName); } // write string value without validation: AtomValueUtils.WritePrimitiveValue(this.XmlWriter, value.Value); }
internal bool WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference metadataTypeReference, bool isOpenPropertyType, bool isWritingCollection, Action beforeValueAction, Action afterValueAction, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment, ProjectedPropertiesAnnotation projectedProperties) { Action action2 = null; string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } this.IncreaseRecursionDepth(); IEdmComplexTypeReference reference = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, metadataTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull(); if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0)) { typeName = null; } SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } Action beforePropertiesAction = beforeValueAction; if (typeName != null) { if (beforeValueAction != null) { if (action2 == null) { action2 = delegate { beforeValueAction(); this.WritePropertyTypeAttribute(typeName); }; } beforePropertiesAction = action2; } else { this.WritePropertyTypeAttribute(typeName); } } if (((base.MessageWriterSettings.WriterBehavior != null) && base.MessageWriterSettings.WriterBehavior.UseV1ProviderBehavior) && !object.ReferenceEquals(projectedProperties, ProjectedPropertiesAnnotation.EmptyProjectedPropertiesMarker)) { IEdmComplexType definition = (IEdmComplexType)reference.Definition; if (base.Model.EpmCachedKeepPrimitiveInContent(definition) == null) { List <string> keptInContentPropertyNames = null; foreach (IEdmProperty property in from p in definition.Properties() where p.Type.IsODataPrimitiveTypeKind() select p) { EntityPropertyMappingAttribute entityPropertyMapping = EpmWriterUtils.GetEntityPropertyMapping(epmSourcePathSegment, property.Name); if ((entityPropertyMapping != null) && entityPropertyMapping.KeepInContent) { if (keptInContentPropertyNames == null) { keptInContentPropertyNames = new List <string>(); } keptInContentPropertyNames.Add(property.Name); } } base.Model.SetAnnotationValue <CachedPrimitiveKeepInContentAnnotation>(definition, new CachedPrimitiveKeepInContentAnnotation(keptInContentPropertyNames)); } } bool flag = this.WriteProperties((reference == null) ? null : reference.ComplexDefinition(), EpmValueCache.GetComplexValueProperties(epmValueCache, complexValue, true), isWritingCollection, beforePropertiesAction, afterValueAction, duplicatePropertyNamesChecker, epmValueCache, epmSourcePathSegment, projectedProperties); this.DecreaseRecursionDepth(); return(flag); }
/// <summary> /// Write the items of a collection in ATOM format. /// </summary> /// <param name="collectionValue">The collection value to write.</param> /// <param name="propertyTypeReference">The type reference of the collection value (or null if not metadata is available).</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <param name="isWritingCollection">true if we are writing a top-level collection instead of an entry.</param> private void WriteCollectionValue( ODataCollectionValue collectionValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, bool isWritingCollection) { Debug.Assert(collectionValue != null, "collectionValue != null"); this.IncreaseRecursionDepth(); // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmCollectionTypeReference collectionTypeReference = (IEdmCollectionTypeReference)TypeNameOracle.ResolveAndValidateTypeForCollectionValue(this.Model, propertyTypeReference, collectionValue, isOpenPropertyType, this.WriterValidator); string collectionItemTypeName; string typeName = this.AtomOutputContext.TypeNameOracle.GetValueTypeNameForWriting(collectionValue, collectionTypeReference, collectionValue.GetAnnotation<SerializationTypeNameAnnotation>(), /*collectionValidator*/ null, out collectionItemTypeName); if (typeName != null) { this.WritePropertyTypeAttribute(typeName); } // COMPAT 4: ATOM and JSON format for collections - The collection format is not formally specified yet, since it's a new feature // If the official format deviates from the current WCFDS behavior we would have to introduce a back compat mode here as well. IEdmTypeReference expectedItemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); CollectionWithoutExpectedTypeValidator collectionValidator = new CollectionWithoutExpectedTypeValidator(collectionItemTypeName); IEnumerable items = collectionValue.Items; if (items != null) { DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = null; foreach (object item in items) { ValidationUtils.ValidateCollectionItem(item, expectedItemTypeReference.IsNullable()); this.XmlWriter.WriteStartElement(AtomConstants.ODataMetadataNamespacePrefix, AtomConstants.ODataCollectionItemElementName, AtomConstants.ODataMetadataNamespace); ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { if (duplicatePropertyNamesChecker == null) { duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); } this.WriteComplexValue( complexValue, expectedItemTypeReference, false, isWritingCollection, null /* beforeValueAction */, null /* afterValueAction */, duplicatePropertyNamesChecker, collectionValidator, null /* projectedProperties */); duplicatePropertyNamesChecker.Clear(); } else { Debug.Assert(!(item is ODataCollectionValue), "!(item is ODataCollectionValue)"); Debug.Assert(!(item is ODataStreamReferenceValue), "!(item is ODataStreamReferenceValue)"); ODataEnumValue enumValue = item as ODataEnumValue; if (enumValue != null) { // Note: Currently there is no way for a user to control enum type's serializationTypeNameAnnotation information when the enum values are part of a collection. this.WriteEnumValue(enumValue, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null); } else { // Note: Currently there is no way for a user to control primitive type information when the primitive values are part of a collection. if (item != null) { this.WritePrimitiveValue(item, collectionValidator, expectedItemTypeReference, /*serializationTypeNameAnnotation*/ null); } else { this.WriteNullCollectionElementValue(expectedItemTypeReference); } } } this.XmlWriter.WriteEndElement(); } } this.DecreaseRecursionDepth(); }
/// <summary> /// Reads the primitive, complex or collection value. /// </summary> /// <param name="expectedValueTypeReference">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> /// <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> internal object ReadNonEntityValue( IEdmTypeReference expectedValueTypeReference, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator, bool validateNullValue) { this.AssertRecursionDepthIsZero(); object nonEntityValue = this.ReadNonEntityValueImplementation( expectedValueTypeReference, duplicatePropertyNamesChecker, collectionValidator, validateNullValue, /*propertyName*/ null); this.AssertRecursionDepthIsZero(); return nonEntityValue; }
/// <summary> /// Writes out the value of a complex property. /// </summary> /// <param name="complexValue">The complex value to write.</param> /// <param name="propertyTypeReference">The metadata type for the complex value.</param> /// <param name="isOpenPropertyType">true if the type name belongs to an open property.</param> /// <param name="duplicatePropertyNamesChecker">The checker instance for duplicate property names.</param> /// <param name="collectionValidator">The collection validator instance to validate the type names and type kinds of collection items; null if no validation is needed.</param> /// <remarks>The current recursion depth should be a value, measured by the number of complex and collection values between /// this complex value and the top-level payload, not including this one.</remarks> internal void WriteComplexValue( ODataComplexValue complexValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(complexValue != null, "complexValue != null"); this.IncreaseRecursionDepth(); // Start the object scope which will represent the entire complex instance this.JsonWriter.StartObjectScope(); // Write the "__metadata" : { "type": "typename" } // But only if we actually have a typename to write, otherwise we need the __metadata to be omitted entirely string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } // resolve the type name to the type; if no type name is specified we will use the // type inferred from metadata IEdmComplexTypeReference complexValueTypeReference = WriterValidationUtils.ResolveTypeNameForWriting(this.Model, propertyTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull(); // If the type is the same as the one specified by the parent collection, omit the type name, since it's not needed. if (typeName != null && collectionValidator != null) { string expectedItemTypeName = collectionValidator.ItemTypeNameFromCollection; if (string.CompareOrdinal(expectedItemTypeName, typeName) == 0) { typeName = null; } } SerializationTypeNameAnnotation serializationTypeNameAnnotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (serializationTypeNameAnnotation != null) { typeName = serializationTypeNameAnnotation.TypeName; } if (typeName != null) { // Write the __metadata object this.JsonWriter.WriteName(JsonConstants.ODataMetadataName); this.JsonWriter.StartObjectScope(); // "type": "typename" this.JsonWriter.WriteName(JsonConstants.ODataMetadataTypeName); this.JsonWriter.WriteValue(typeName); // End the __metadata this.JsonWriter.EndObjectScope(); } // Write the properties of the complex value as usual. Note we do not allow complex types to contain named stream properties. this.WriteProperties( complexValueTypeReference == null ? null : complexValueTypeReference.ComplexDefinition(), complexValue.Properties, true /* isComplexValue */, duplicatePropertyNamesChecker, null /*projectedProperties */); // End the object scope which represents the complex instance this.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }
/// <summary> /// Reads a collection value. /// </summary> /// <param name="collectionValueTypeReference">The collection type reference of the value.</param> /// <param name="payloadTypeName">The type name read from the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param> /// <returns>The value of the collection.</returns> /// <remarks> /// Pre-Condition: Fails if the current node is not a JsonNodeType.StartObject /// Post-Condition: almost anything - the node after the collection value (after the EndObject) /// </remarks> private ODataCollectionValue ReadCollectionValueImplementation( IEdmCollectionTypeReference collectionValueTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { DebugUtils.CheckNoExternalCallers(); Debug.Assert( collectionValueTypeReference == null || collectionValueTypeReference.IsNonEntityCollectionType(), "If the metadata is specified it must denote a Collection for this method to work."); this.JsonReader.AssertNotBuffering(); ODataVersionChecker.CheckCollectionValue(this.Version); this.IncreaseRecursionDepth(); // Read over the start object this.JsonReader.ReadStartObject(); ODataCollectionValue collectionValue = new ODataCollectionValue(); collectionValue.TypeName = collectionValueTypeReference != null?collectionValueTypeReference.ODataFullName() : payloadTypeName; if (serializationTypeNameAnnotation != null) { collectionValue.SetAnnotation(serializationTypeNameAnnotation); } List <object> items = null; bool metadataPropertyFound = false; while (this.JsonReader.NodeType == JsonNodeType.Property) { string propertyName = this.JsonReader.ReadPropertyName(); if (string.CompareOrdinal(JsonConstants.ODataMetadataName, propertyName) == 0) { // __metadata property if (metadataPropertyFound) { throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper(JsonConstants.ODataMetadataName)); } metadataPropertyFound = true; // Note that we don't need to read the type name again, as we've already read it above in FindTypeNameInPayload. // There's nothing else of interest in the __metadata for collections. this.JsonReader.SkipValue(); } else if (string.CompareOrdinal(JsonConstants.ODataResultsName, propertyName) == 0) { // results property if (items != null) { throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper(JsonConstants.ODataResultsName)); } items = new List <object>(); this.JsonReader.ReadStartArray(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); IEdmTypeReference itemType = null; if (collectionValueTypeReference != null) { itemType = collectionValueTypeReference.CollectionDefinition().ElementType; } // NOTE: we do not support reading Verbose JSON without metadata right now so we always have an expected item type; // The collection validator is always null. CollectionWithoutExpectedTypeValidator collectionValidator = null; while (this.JsonReader.NodeType != JsonNodeType.EndArray) { object itemValue = this.ReadNonEntityValueImplementation( itemType, duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /*propertyName*/ null); // Validate the item (for example that it's not null) ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */); // Note that the ReadNonEntityValue already validated that the actual type of the value matches // the expected type (the itemType). items.Add(itemValue); } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndArray, "The results value must end with an end array."); this.JsonReader.ReadEndArray(); } else { // Skip over any other property in the collection object this.JsonReader.SkipValue(); } } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndObject, "After all properties of Collection wrapper are read the EndObject node is expected."); this.JsonReader.ReadEndObject(); if (items == null) { // We didn't find any results property. All collections must have the results property. throw new ODataException(ODataErrorStrings.ODataJsonPropertyAndValueDeserializer_CollectionWithoutResults); } collectionValue.Items = new ReadOnlyEnumerable(items); this.JsonReader.AssertNotBuffering(); this.DecreaseRecursionDepth(); return(collectionValue); }
/// <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); }
/// <summary> /// Reads an item in the collection. /// </summary> /// <param name="expectedItemTypeReference">The expected type of the item to read.</param> /// <param name="collectionValidator">The collection validator instance if no expected item type has been specified; otherwise null.</param> /// <returns>The value of the collection item that was read; this can be an ODataComplexValue, a primitive value or 'null'.</returns> /// <remarks> /// Pre-Condition: The first node of the item in the collection /// NOTE: this method will throw if the node is not /// JsonNodeType.StartObject: for a complex item /// JsonNodeType.PrimitiveValue: for a primitive item /// Post-Condition: The reader is positioned on the first node of the next item or an EndArray node if there are no more items in the collection /// </remarks> internal object ReadCollectionItem(IEdmTypeReference expectedItemTypeReference, CollectionWithoutExpectedTypeValidator collectionValidator) { DebugUtils.CheckNoExternalCallers(); Debug.Assert( expectedItemTypeReference == null || expectedItemTypeReference.IsODataPrimitiveTypeKind() || expectedItemTypeReference.IsODataComplexTypeKind(), "If an expected type is specified, it must be a primitive or complex type."); this.JsonReader.AssertNotBuffering(); object item = this.ReadNonEntityValue( expectedItemTypeReference, this.duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true); this.JsonReader.AssertNotBuffering(); return(item); }
/// <summary> /// Read a collection from the reader. /// </summary> /// <param name="collectionTypeReference">The type of the collection to read (or null if no type is available).</param> /// <param name="payloadTypeName">The name of the collection type specified in the payload.</param> /// <param name="serializationTypeNameAnnotation">The serialization type name for the collection value (possibly null).</param> /// <returns>The value read from the payload.</returns> /// <remarks> /// Pre-Condition: XmlNodeType.Element - the element to read the value for. /// XmlNodeType.Attribute - an attribute on the element to read the value for. /// Post-Condition: XmlNodeType.Element - the element was empty. /// XmlNodeType.EndElement - the element had some value. /// /// Note that this method will not read null values, those should be handled by the caller already. /// </remarks> private ODataCollectionValue ReadCollectionValue( IEdmCollectionTypeReference collectionTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { this.AssertXmlCondition(XmlNodeType.Element, XmlNodeType.Attribute); Debug.Assert( collectionTypeReference == null || collectionTypeReference.IsNonEntityODataCollectionTypeKind(), "If the metadata is specified it must denote a collection for this method to work."); this.IncreaseRecursionDepth(); ODataCollectionValue collectionValue = new ODataCollectionValue(); // If we have a metadata type for the collection, use that type name // otherwise use the type name from the payload (if there was any). collectionValue.TypeName = collectionTypeReference == null ? payloadTypeName : collectionTypeReference.ODataFullName(); if (serializationTypeNameAnnotation != null) { collectionValue.SetAnnotation(serializationTypeNameAnnotation); } // Move to the element (so that if we were on an attribute we can test the element for being empty) this.XmlReader.MoveToElement(); List <object> items = new List <object>(); // Empty collections are valid - they have no items if (!this.XmlReader.IsEmptyElement) { // Read over the collection element to its first child node (or end-element) this.XmlReader.ReadStartElement(); IEdmTypeReference itemTypeReference = collectionTypeReference == null ? null : collectionTypeReference.ElementType(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); CollectionWithoutExpectedTypeValidator collectionValidator = null; if (collectionTypeReference == null) { // Parse the type name from the payload (if any), extract the item type name and construct a collection validator string itemTypeName = payloadTypeName == null ? null : EdmLibraryExtensions.GetCollectionItemTypeName(payloadTypeName); collectionValidator = new CollectionWithoutExpectedTypeValidator(itemTypeName); } do { switch (this.XmlReader.NodeType) { case XmlNodeType.Element: if (this.XmlReader.NamespaceEquals(this.XmlReader.ODataNamespace)) { if (!this.XmlReader.LocalNameEquals(this.ODataCollectionItemElementName)) { throw new ODataException(o.Strings.ODataAtomPropertyAndValueDeserializer_InvalidCollectionElement(this.XmlReader.LocalName, this.XmlReader.ODataNamespace)); } // We don't support EPM for collections so it is fine to say that EPM is not present object itemValue = this.ReadNonEntityValueImplementation( itemTypeReference, duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /*epmPresent*/ false); // read over the end tag of the element or the start tag if the element was empty. this.XmlReader.Read(); // Validate the item (for example that it's not null) ValidationUtils.ValidateCollectionItem(itemValue, false /* isStreamable */); // Note that the ReadNonEntityValue already validated that the actual type of the value matches // the expected type (the itemType). items.Add(itemValue); } else { this.XmlReader.Skip(); } break; case XmlNodeType.EndElement: // End of the collection. break; default: // Non-element so for example a text node, just ignore this.XmlReader.Skip(); break; } }while (this.XmlReader.NodeType != XmlNodeType.EndElement); } collectionValue.Items = new ReadOnlyEnumerable(items); this.AssertXmlCondition(true, XmlNodeType.EndElement); Debug.Assert(collectionValue != null, "The method should never return null since it doesn't handle null values."); this.DecreaseRecursionDepth(); return(collectionValue); }
internal void WriteComplexValue(ODataComplexValue complexValue, IEdmTypeReference propertyTypeReference, bool isOpenPropertyType, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, CollectionWithoutExpectedTypeValidator collectionValidator) { this.IncreaseRecursionDepth(); base.JsonWriter.StartObjectScope(); string typeName = complexValue.TypeName; if (collectionValidator != null) { collectionValidator.ValidateCollectionItem(typeName, EdmTypeKind.Complex); } IEdmComplexTypeReference type = WriterValidationUtils.ResolveTypeNameForWriting(base.Model, propertyTypeReference, ref typeName, EdmTypeKind.Complex, isOpenPropertyType).AsComplexOrNull(); if (((typeName != null) && (collectionValidator != null)) && (string.CompareOrdinal(collectionValidator.ItemTypeNameFromCollection, typeName) == 0)) { typeName = null; } SerializationTypeNameAnnotation annotation = complexValue.GetAnnotation <SerializationTypeNameAnnotation>(); if (annotation != null) { typeName = annotation.TypeName; } if (typeName != null) { base.JsonWriter.WriteName("__metadata"); base.JsonWriter.StartObjectScope(); base.JsonWriter.WriteName("type"); base.JsonWriter.WriteValue(typeName); base.JsonWriter.EndObjectScope(); } this.WriteProperties((type == null) ? null : type.ComplexDefinition(), complexValue.Properties, true, duplicatePropertyNamesChecker, null); base.JsonWriter.EndObjectScope(); this.DecreaseRecursionDepth(); }