/// <summary> /// Writes a collection item (either primitive or complex) /// </summary> /// <param name="item">The collection item to write.</param> protected override void WriteItemImplementation(object item) { if (item == null) { this.jsonWriter.WriteValue(null); } else { ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { ODataJsonWriterUtils.WriteComplexValue(this.jsonWriter, this.MetadataProvider, complexValue, null, false, this.Version); } else { ODataMultiValue multiValue = item as ODataMultiValue; if (multiValue != null) { throw new ODataException(Strings.ODataCollectionWriter_MultiValuesNotSupportedInCollections); } ODataJsonWriterUtils.WritePrimitiveValue(this.jsonWriter, item, null); } } }
/// <summary> /// Writes a name/value pair for a property. /// </summary> /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="property">The property to write out.</param> /// <param name="owningType">The type owning the property (or null if no metadata is available).</param> /// <param name="version">The protocol version used for writing.</param> private static void WriteProperty( JsonWriter jsonWriter, DataServiceMetadataProviderWrapper metadata, ODataProperty property, ResourceType owningType, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(jsonWriter != null, "jsonWriter != null"); ValidationUtils.ValidateProperty(property); ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(property.Name, owningType); bool isOpenPropertyType = owningType != null && owningType.IsOpenType && resourceProperty == null; jsonWriter.WriteName(property.Name); object value = property.Value; if (value == null) { // verify that MultiValue properties are not null if (resourceProperty != null && resourceProperty.Kind == ResourcePropertyKind.MultiValue) { throw new ODataException(Strings.ODataWriter_MultiValuePropertiesMustNotHaveNullValue(resourceProperty.Name)); } jsonWriter.WriteValue(null); } else { ResourceType resourcePropertyType = resourceProperty == null ? null : resourceProperty.ResourceType; ODataComplexValue complexValue = value as ODataComplexValue; if (complexValue != null) { WriteComplexValue(jsonWriter, metadata, complexValue, resourcePropertyType, isOpenPropertyType, version); } else { ODataMultiValue multiValue = value as ODataMultiValue; if (multiValue != null) { ODataVersionChecker.CheckMultiValueProperties(version, property.Name); WriteMultiValue(jsonWriter, metadata, multiValue, resourcePropertyType, isOpenPropertyType, version); } else { WritePrimitiveValue(jsonWriter, value, resourcePropertyType); } } } }
/// <summary> /// Returns the items for the specified multivalue. /// </summary> /// <param name="epmValueCache">The EPM value cache to use (can be null).</param> /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param> /// <param name="multiValue">The multivalue to get the items for.</param> /// <param name="writingContent">If we're writing content of an entry or not.</param> /// <returns>The items enumeration for the multivalue.</returns> internal static IEnumerable GetMultiValueItems( EpmValueCache epmValueCache, EpmSourcePathSegment sourcePathSegment, ODataMultiValue multiValue, bool writingContent) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(multiValue != null, "multiValue != null"); Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist."); if (epmValueCache == null) { return(multiValue.Items); } else { return(epmValueCache.GetMultiValueItems(sourcePathSegment, multiValue, writingContent)); } }
/// <summary> /// Returns the items for the specified multivalue. /// </summary> /// <param name="epmValueCache">The EPM value cache to use (can be null).</param> /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param> /// <param name="multiValue">The multivalue to get the items for.</param> /// <param name="writingContent">If we're writing content of an entry or not.</param> /// <returns>The items enumeration for the multivalue.</returns> internal static IEnumerable GetMultiValueItems( EpmValueCache epmValueCache, EpmSourcePathSegment sourcePathSegment, ODataMultiValue multiValue, bool writingContent) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(multiValue != null, "multiValue != null"); Debug.Assert(writingContent || epmValueCache != null, "If we're not writing content, then the EPM value cache must exist."); if (epmValueCache == null) { return multiValue.Items; } else { return epmValueCache.GetMultiValueItems(sourcePathSegment, multiValue, writingContent); } }
/// <summary> /// Writes a collection item (either primitive or complex) /// </summary> /// <param name="item">The collection item to write.</param> protected override void WriteItemImplementation(object item) { // <d:element> this.writer.WriteStartElement(AtomConstants.ODataCollectionItemElementName, AtomConstants.ODataNamespace); if (item == null) { // NOTE can't use ODataAtomWriterUtils.WriteNullAttribute because that method assumes the // default 'm' prefix for the metadata namespace. this.writer.WriteAttributeString( AtomConstants.ODataNullAttributeName, AtomConstants.ODataMetadataNamespace, AtomConstants.AtomTrueLiteral); } else { ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { ODataAtomWriterUtils.WriteComplexValue(this.writer, this.MetadataProvider, complexValue, null, false, true, this.Version, null, null); } else { ODataMultiValue multiValue = item as ODataMultiValue; if (multiValue != null) { throw new ODataException(Strings.ODataCollectionWriter_MultiValuesNotSupportedInCollections); } ODataAtomWriterUtils.WritePrimitiveValue(this.writer, item, null); } } // </d:element> this.writer.WriteEndElement(); }
/// <summary> /// Returns the items for the specified multivalue. /// </summary> /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param> /// <param name="multiValue">The multivalue to get the items for.</param> /// <param name="writingContent">true if we're writing entry content or false when writing out-of-content EPM.</param> /// <returns>The items enumeration for the multivalue.</returns> private IEnumerable GetMultiValueItems(EpmSourcePathSegment sourcePathSegment, ODataMultiValue multiValue, bool writingContent) { Debug.Assert(writingContent || sourcePathSegment != null, "sourcePathSegment must be specified when writing out-of-content."); Debug.Assert(multiValue != null, "multiValue != null"); // If we're writing into content we don't want to populate the cache if it's not already populated. // The goal is to behave the same with and without EPM. if (writingContent && this.epmValuesCache == null) { return(multiValue.Items); } object cachedItemsValue; if (this.epmValuesCache != null && this.epmValuesCache.TryGetValue(sourcePathSegment, out cachedItemsValue)) { Debug.Assert(cachedItemsValue is List <object>, "The cached value for multi value must be a List of object"); return((IEnumerable)cachedItemsValue); } IEnumerable items = multiValue.Items; List <object> cachedItems = null; if (items != null) { cachedItems = new List <object>(); foreach (object item in items) { // If the value is a complex value, store it as EpmMultiValueItemCache instance, so that we have a place // to cache the enumeration of properties on that complex value (and possible other nested complex/multi values). if (item is ODataComplexValue) { cachedItems.Add(new EpmMultiValueItemCache(item)); } else { // Otherwise it should be a primitive value and thus we can just cache the value itself as it won't have any children cachedItems.Add(item); } } } if (this.epmValuesCache == null) { this.epmValuesCache = new Dictionary <EpmSourcePathSegment, object>(ReferenceEqualityComparer <EpmSourcePathSegment> .Instance); } this.epmValuesCache.Add(sourcePathSegment, cachedItems); return(cachedItems); }
/// <summary> /// Returns the items for the specified multivalue. /// </summary> /// <param name="sourcePathSegment">The source path segment for the property which has this multivalue.</param> /// <param name="multiValue">The multivalue to get the items for.</param> /// <param name="writingContent">true if we're writing entry content or false when writing out-of-content EPM.</param> /// <returns>The items enumeration for the multivalue.</returns> private IEnumerable GetMultiValueItems(EpmSourcePathSegment sourcePathSegment, ODataMultiValue multiValue, bool writingContent) { Debug.Assert(writingContent || sourcePathSegment != null, "sourcePathSegment must be specified when writing out-of-content."); Debug.Assert(multiValue != null, "multiValue != null"); // If we're writing into content we don't want to populate the cache if it's not already populated. // The goal is to behave the same with and without EPM. if (writingContent && this.epmValuesCache == null) { return multiValue.Items; } object cachedItemsValue; if (this.epmValuesCache != null && this.epmValuesCache.TryGetValue(sourcePathSegment, out cachedItemsValue)) { Debug.Assert(cachedItemsValue is List<object>, "The cached value for multi value must be a List of object"); return (IEnumerable)cachedItemsValue; } IEnumerable items = multiValue.Items; List<object> cachedItems = null; if (items != null) { cachedItems = new List<object>(); foreach (object item in items) { // If the value is a complex value, store it as EpmMultiValueItemCache instance, so that we have a place // to cache the enumeration of properties on that complex value (and possible other nested complex/multi values). if (item is ODataComplexValue) { cachedItems.Add(new EpmMultiValueItemCache(item)); } else { // Otherwise it should be a primitive value and thus we can just cache the value itself as it won't have any children cachedItems.Add(item); } } } if (this.epmValuesCache == null) { this.epmValuesCache = new Dictionary<EpmSourcePathSegment, object>(ReferenceEqualityComparer<EpmSourcePathSegment>.Instance); } this.epmValuesCache.Add(sourcePathSegment, cachedItems); return cachedItems; }
/// <summary> /// Writes out the value of a MultiValue property. /// </summary> /// <param name="jsonWriter">The <see cref="JsonWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="multiValue">The bag value to write.</param> /// <param name="metadataType">The metadata type for the MultiValue.</param> /// <param name="isOpenPropertyType">True if the type name belongs to an open property.</param> /// <param name="version">The protocol version used for writing.</param> private static void WriteMultiValue( JsonWriter jsonWriter, DataServiceMetadataProviderWrapper metadata, ODataMultiValue multiValue, ResourceType metadataType, bool isOpenPropertyType, ODataVersion version) { Debug.Assert(multiValue != null, "multiValue != null"); // Start the object scope which will represent the entire MultiValue instance jsonWriter.StartObjectScope(); // "__metadata": { "type": "typename" } // If the MultiValue has type information write out the metadata and the type in it. string typeName = multiValue.TypeName; // resolve the type name to the resource type; if no type name is specified we will use the // type inferred from metadata ResourceType multiValueType = MetadataUtils.ResolveTypeName(metadata, metadataType, ref typeName, ResourceTypeKind.MultiValue, isOpenPropertyType); if (typeName != null) { // Write the __metadata object jsonWriter.WriteName(JsonConstants.ODataMetadataName); jsonWriter.StartObjectScope(); // "type": "typename" jsonWriter.WriteName(JsonConstants.ODataMetadataTypeName); jsonWriter.WriteValue(typeName); // End the __metadata jsonWriter.EndObjectScope(); } // "results": [ // This represents the array of items in the MultiValue jsonWriter.WriteDataArrayName(); jsonWriter.StartArrayScope(); // Iterate through the MultiValue items and write them out (treat null Items as an empty enumeration) IEnumerable items = multiValue.Items; if (items != null) { ResourceType expectedItemType = multiValueType == null ? null : ((MultiValueResourceType)multiValueType).ItemType; foreach (object item in items) { ValidationUtils.ValidateMultiValueItem(item); ODataComplexValue itemAsComplexValue = item as ODataComplexValue; if (itemAsComplexValue != null) { WriteComplexValue(jsonWriter, metadata, itemAsComplexValue, expectedItemType, false, version); } else { ODataMultiValue itemAsMultiValue = item as ODataMultiValue; if (itemAsMultiValue != null) { throw new ODataException(Strings.ODataWriter_NestedMultiValuesAreNotSupported); } else { WritePrimitiveValue(jsonWriter, item, expectedItemType); } } } } // End the array scope which holds the items jsonWriter.EndArrayScope(); // End the object scope which holds the entire MultiValue jsonWriter.EndObjectScope(); }
/// <summary> /// Write the items in a MultiValue in ATOM format. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="multiValue">The MultiValue to write.</param> /// <param name="resourcePropertyType">The resource type of the multi 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 collection instead of an entry.</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmSourcePathSegment">The EPM source path segment which points to the multivalue property we're writing. (can be null)</param> private static void WriteMultiValue( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataMultiValue multiValue, ResourceType resourcePropertyType, bool isOpenPropertyType, bool isWritingCollection, ODataVersion version, EpmValueCache epmValueCache, EpmSourcePathSegment epmSourcePathSegment) { Debug.Assert(multiValue != null, "multiValue != null"); string typeName = multiValue.TypeName; // resolve the type name to the resource type; if no type name is specified we will use the // type inferred from metadata MultiValueResourceType multiValueType = (MultiValueResourceType)MetadataUtils.ResolveTypeName(metadata, resourcePropertyType, ref typeName, ResourceTypeKind.MultiValue, isOpenPropertyType); if (typeName != null) { WritePropertyTypeAttribute(writer, typeName); } ResourceType expectedItemType = multiValueType == null ? null : multiValueType.ItemType; IEnumerable items = EpmValueCache.GetMultiValueItems(epmValueCache, epmSourcePathSegment, multiValue, true); if (items != null) { foreach (object itemValue in items) { object item; EpmMultiValueItemCache epmItemCache = itemValue as EpmMultiValueItemCache; if (epmItemCache != null) { item = epmItemCache.ItemValue; } else { item = itemValue; } ValidationUtils.ValidateMultiValueItem(item); writer.WriteStartElement(AtomConstants.ODataNamespacePrefix, AtomConstants.ODataMultiValueItemElementName, AtomConstants.ODataNamespace); ODataComplexValue complexValue = item as ODataComplexValue; if (complexValue != null) { WriteComplexValue(writer, metadata, complexValue, expectedItemType, false, isWritingCollection, version, epmItemCache, epmSourcePathSegment); } else { ODataMultiValue multiValueItem = item as ODataMultiValue; if (multiValueItem != null) { throw new ODataException(Strings.ODataWriter_NestedMultiValuesAreNotSupported); } else { AtomValueUtils.WritePrimitiveValue(writer, item, expectedItemType); } } writer.WriteEndElement(); } } }
/// <summary> /// Writes a single property in ATOM format. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to write to.</param> /// <param name="metadata">The metadata provider to use or null if no metadata is available.</param> /// <param name="property">The property to write out.</param> /// <param name="owningType">The type owning the property (or null if no metadata is available).</param> /// <param name="version">The protocol version used for writing.</param> /// <param name="isTopLevel">True if writing a top-level property payload; otherwise false.</param> /// <param name="isWritingCollection">True if we are writing a collection instead of an entry.</param> /// <param name="epmValueCache">Cache of values used in EPM so that we avoid multiple enumerations of properties/items. (can be null)</param> /// <param name="epmParentSourcePathSegment">The EPM source path segment which points to the property which sub-property we're writing. (can be null)</param> internal static void WriteProperty( XmlWriter writer, DataServiceMetadataProviderWrapper metadata, ODataProperty property, ResourceType owningType, ODataVersion version, bool isTopLevel, bool isWritingCollection, EpmValueCache epmValueCache, EpmSourcePathSegment epmParentSourcePathSegment) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(writer != null, "writer != null"); ValidationUtils.ValidateProperty(property); ResourceProperty resourceProperty = ValidationUtils.ValidatePropertyDefined(property.Name, owningType); EpmSourcePathSegment epmSourcePathSegment = null; if (epmParentSourcePathSegment != null) { epmSourcePathSegment = epmParentSourcePathSegment.SubProperties.Where(subProperty => subProperty.PropertyName == property.Name).FirstOrDefault(); } object value = property.Value; // TODO: If we implement validation or type conversions the value needs to be converted here // since the next method call needs to know if the value is a string or not in some cases. // If EPM tells us to skip this property in content, then we're done here. if (!ShouldWritePropertyInContent(value, epmSourcePathSegment, version)) { return; } // <d:propertyname> writer.WriteStartElement( isWritingCollection ? string.Empty : AtomConstants.ODataNamespacePrefix, property.Name, AtomConstants.ODataNamespace); if (isTopLevel) { WriteDefaultNamespaceAttributes(writer, DefaultNamespaceFlags.OData | DefaultNamespaceFlags.ODataMetadata); } // Null property value. if (value == null) { // verify that MultiValue properties are not null if (resourceProperty != null && resourceProperty.Kind == ResourcePropertyKind.MultiValue) { throw new ODataException(Strings.ODataWriter_MultiValuePropertiesMustNotHaveNullValue(resourceProperty.Name)); } ODataAtomWriterUtils.WriteNullAttribute(writer); } else { ODataComplexValue complexValue = value as ODataComplexValue; ResourceType resourcePropertyType = resourceProperty == null ? null : resourceProperty.ResourceType; bool isOpenPropertyType = owningType != null && owningType.IsOpenType && resourceProperty == null; // Complex properties are written recursively. if (complexValue != null) { WriteComplexValue(writer, metadata, complexValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment); } else { ODataMultiValue multiValue = value as ODataMultiValue; if (multiValue != null) { ODataVersionChecker.CheckMultiValueProperties(version, property.Name); WriteMultiValue(writer, metadata, multiValue, resourcePropertyType, isOpenPropertyType, isWritingCollection, version, epmValueCache, epmSourcePathSegment); } else { WritePrimitiveValue(writer, value, resourcePropertyType); } } } // </d:propertyname> writer.WriteEndElement(); }
private void WriteValue(object value) { ODataComplexValue complexValue = value as ODataComplexValue; if (complexValue != null) { this.writer.WriteLine("ODataComplexValue"); this.writer.Indent++; this.writer.WriteLine("TypeName: " + (complexValue.TypeName ?? "<null>")); this.WriteProperties(complexValue.Properties); this.writer.Indent--; return; } ODataMultiValue multiValue = value as ODataMultiValue; if (multiValue != null) { this.writer.WriteLine("ODataMultiValue"); this.writer.Indent++; this.writer.WriteLine("TypeName: " + (multiValue.TypeName ?? "<null>")); this.writer.WriteLine("Items:"); this.writer.Indent++; foreach (object item in multiValue.Items) { this.WriteValue(item); } this.writer.Indent--; this.writer.Indent--; return; } ODataStreamReferenceValue streamReferenceValue = value as ODataStreamReferenceValue; if (streamReferenceValue != null) { this.writer.WriteLine("ODataStreamReferenceValue"); this.writer.Indent++; if (streamReferenceValue.ReadLink != null) { this.writer.WriteLine("ReadLink: " + streamReferenceValue.ReadLink.AbsoluteUri); } if (streamReferenceValue.EditLink != null) { this.writer.WriteLine("EditLink: " + streamReferenceValue.EditLink.AbsoluteUri); } this.writer.Indent--; return; } if (value == null) { this.writer.WriteLine("null"); } else { this.writer.WriteLine(value.ToString()); } }