public void WriteInstanceAnnotation_ForCollectionShouldUseCollectionCodePath() { var collectionValue = new ODataCollectionValue() { TypeName = "Collection(String)" }; collectionValue.SetAnnotation(new SerializationTypeNameAnnotation() { TypeName = null }); const string term = "some.term"; var verifierCalls = 0; this.jsonWriter.WriteNameVerifier = (name) => { name.Should().Be("@" + term); verifierCalls++; }; this.valueWriter.WriteCollectionVerifier = (value, reference, valueTypeReference, isTopLevelProperty, isInUri, isOpenProperty) => { value.Should().Be(collectionValue); reference.Should().BeNull(); valueTypeReference.Should().NotBeNull(); valueTypeReference.IsCollection().Should().BeTrue(); valueTypeReference.AsCollection().ElementType().IsString().Should().BeTrue(); isOpenProperty.Should().BeTrue(); isTopLevelProperty.Should().BeFalse(); isInUri.Should().BeFalse(); verifierCalls.Should().Be(1); verifierCalls++; }; this.jsonLightInstanceAnnotationWriter.WriteInstanceAnnotation(new ODataInstanceAnnotation(term, collectionValue)); verifierCalls.Should().Be(2); }
/// <summary> /// Adds the type name annotations required for proper json light serialization. /// </summary> /// <param name="value">The collection value for which the annotations have to be added.</param> /// <param name="metadataLevel">The OData metadata level of the response.</param> protected internal static void AddTypeNameAnnotationAsNeeded(ODataCollectionValue value, ODataMetadataLevel metadataLevel) { // ODataLib normally has the caller decide whether or not to serialize properties by leaving properties // null when values should not be serialized. The TypeName property is different and should always be // provided to ODataLib to enable model validation. A separate annotation is used to decide whether or not // to serialize the type name (a null value prevents serialization). Contract.Assert(value != null); // Only add an annotation if we want to override ODataLib's default type name serialization behavior. if (ShouldAddTypeNameAnnotation(metadataLevel)) { string typeName; // Provide the type name to serialize (or null to force it not to serialize). if (ShouldSuppressTypeNameSerialization(metadataLevel)) { typeName = null; } else { typeName = value.TypeName; } value.SetAnnotation <SerializationTypeNameAnnotation>(new SerializationTypeNameAnnotation { TypeName = typeName }); } }
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); }
public void BuildPropertyContextUriForCollectionPropertyValueWithNonNullAnnotation() { ODataCollectionValue value = new ODataCollectionValue { TypeName = "FQNS.FromObject" }; value.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = "FQNS.FromAnnotation" }); var contextUri = this.CreatePropertyContextUri(value); contextUri.OriginalString.Should().Be(BuildExpectedContextUri("#FQNS.FromAnnotation")); }
private ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, List <object> visitedComplexTypeObjects) { PrimitiveType type; string edmType; Func <object, object> valueConverter = null; Func <object, ODataComplexValue> func2 = null; WebUtil.ValidateCollection(collectionItemType, value, propertyName); bool flag = PrimitiveType.TryGetPrimitiveType(collectionItemType, out type); ODataCollectionValue value2 = new ODataCollectionValue(); IEnumerable enumerable = (IEnumerable)value; if (flag) { edmType = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType); if (valueConverter == null) { valueConverter = delegate(object val) { WebUtil.ValidateCollectionItem(val); WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType); return(GetPrimitiveValue(val, collectionItemType)); }; } value2.Items = Util.GetEnumerable <object>(enumerable, valueConverter); } else { edmType = this.requestInfo.ResolveNameFromType(collectionItemType); if (func2 == null) { func2 = delegate(object val) { WebUtil.ValidateCollectionItem(val); WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType); return(this.CreateODataComplexValue(collectionItemType, val, propertyName, true, visitedComplexTypeObjects)); }; } value2.Items = Util.GetEnumerable <ODataComplexValue>(enumerable, func2); } string str2 = (edmType == null) ? null : string.Format(CultureInfo.InvariantCulture, "Collection({0})", new object[] { edmType }); SerializationTypeNameAnnotation annotation = new SerializationTypeNameAnnotation { TypeName = str2 }; value2.SetAnnotation <SerializationTypeNameAnnotation>(annotation); return(value2); }
public void TypeNameShouldComeFromSerializationTypeNameAnnotationForCollectionValue() { var stna = new SerializationTypeNameAnnotation() { TypeName = "FromSTNA" }; var value = new ODataCollectionValue() { TypeName = "Collection(Edm.String)" }; value.SetAnnotation(stna); this.typeNameOracle.GetValueTypeNameForWriting(value, EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(true)), EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(false)), /* isOpenProperty*/ false).Should().Be("FromSTNA"); }
/// <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); }
/// <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; }
/// <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 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.StartArray /// Post-Condition: almost anything - the node after the collection value (after the EndArray) /// </remarks> private ODataCollectionValue ReadCollectionValue( IEdmCollectionTypeReference collectionValueTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { Debug.Assert( collectionValueTypeReference == null || collectionValueTypeReference.IsNonEntityCollectionType(), "If the metadata is specified it must denote a Collection for this method to work."); this.IncreaseRecursionDepth(); // Read over the start array this.JsonReader.ReadStartArray(); ODataCollectionValue collectionValue = new ODataCollectionValue(); collectionValue.TypeName = collectionValueTypeReference != null ? collectionValueTypeReference.FullName() : payloadTypeName; if (serializationTypeNameAnnotation != null) { collectionValue.SetAnnotation(serializationTypeNameAnnotation); } if (collectionValueTypeReference != null) { collectionValue.SetAnnotation(new ODataTypeAnnotation(collectionValueTypeReference)); } List<object> items = new List<object>(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = this.CreateDuplicatePropertyNamesChecker(); IEdmTypeReference itemType = null; if (collectionValueTypeReference != null) { itemType = collectionValueTypeReference.CollectionDefinition().ElementType; } // NOTE: we do not support reading JSON Light 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( /*payloadTypeName*/ null, itemType, duplicatePropertyNamesChecker, collectionValidator, /*validateNullValue*/ true, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, /*propertyName*/ null); // Validate the item (for example that it's not null) ValidationUtils.ValidateCollectionItem(itemValue, itemType.IsNullable()); // 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(); collectionValue.Items = new ReadOnlyEnumerable(items); this.DecreaseRecursionDepth(); return collectionValue; }
private ODataCollectionValue ReadCollectionValueImplementation(IEdmCollectionTypeReference collectionValueTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation) { ODataVersionChecker.CheckCollectionValue(base.Version); this.IncreaseRecursionDepth(); base.JsonReader.ReadStartObject(); ODataCollectionValue value2 = new ODataCollectionValue { TypeName = (collectionValueTypeReference != null) ? collectionValueTypeReference.ODataFullName() : payloadTypeName }; if (serializationTypeNameAnnotation != null) { value2.SetAnnotation <SerializationTypeNameAnnotation>(serializationTypeNameAnnotation); } List <object> sourceEnumerable = null; bool flag = false; while (base.JsonReader.NodeType == JsonNodeType.Property) { string strB = base.JsonReader.ReadPropertyName(); if (string.CompareOrdinal("__metadata", strB) == 0) { if (flag) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper("__metadata")); } flag = true; base.JsonReader.SkipValue(); } else { if (string.CompareOrdinal("results", strB) == 0) { if (sourceEnumerable != null) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_MultiplePropertiesInCollectionWrapper("results")); } sourceEnumerable = new List <object>(); base.JsonReader.ReadStartArray(); DuplicatePropertyNamesChecker duplicatePropertyNamesChecker = base.CreateDuplicatePropertyNamesChecker(); IEdmTypeReference expectedTypeReference = null; if (collectionValueTypeReference != null) { expectedTypeReference = collectionValueTypeReference.CollectionDefinition().ElementType; } CollectionWithoutExpectedTypeValidator collectionValidator = null; while (base.JsonReader.NodeType != JsonNodeType.EndArray) { object item = this.ReadNonEntityValueImplementation(expectedTypeReference, duplicatePropertyNamesChecker, collectionValidator, true); ValidationUtils.ValidateCollectionItem(item, false); sourceEnumerable.Add(item); } base.JsonReader.ReadEndArray(); continue; } base.JsonReader.SkipValue(); } } base.JsonReader.ReadEndObject(); if (sourceEnumerable == null) { throw new ODataException(Microsoft.Data.OData.Strings.ODataJsonPropertyAndValueDeserializer_CollectionWithoutResults); } value2.Items = new ReadOnlyEnumerable(sourceEnumerable); this.DecreaseRecursionDepth(); return(value2); }
/// <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.FullName(); 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); }
/// <summary> /// Creates and returns an ODataCollectionValue from the given value. /// </summary> /// <param name="collectionItemType">The type of the value.</param> /// <param name="propertyName">If the value is a property, then it represents the name of the property. Can be null, for non-property.</param> /// <param name="value">The value.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <param name="isDynamicProperty">Whether this collection property is a dynamic property</param> /// <returns>An ODataCollectionValue representing the given value.</returns> internal ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, HashSet <object> visitedComplexTypeObjects, bool isDynamicProperty) { Debug.Assert(collectionItemType != null, "collectionItemType != null"); WebUtil.ValidateCollection(collectionItemType, value, propertyName, isDynamicProperty); PrimitiveType ptype; bool isCollectionOfPrimitiveTypes = PrimitiveType.TryGetPrimitiveType(collectionItemType, out ptype); ODataCollectionValue collection = new ODataCollectionValue(); IEnumerable enumerablePropertyValue = (IEnumerable)value; string collectionItemTypeName; string collectionTypeName; if (isCollectionOfPrimitiveTypes) { collectionItemTypeName = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType); if (enumerablePropertyValue != null) { collection.Items = Util.GetEnumerable( enumerablePropertyValue, (val) => { WebUtil.ValidateCollectionItem(val); WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType); return(ConvertPrimitiveValueToRecognizedODataType(val, collectionItemType)); }); } // TypeName for primitives should be the EDM name since that's what we will be able to look up in the model collectionTypeName = collectionItemTypeName; } else { Type collectionItemTypeTmp = Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType; bool areEnumItems = collectionItemTypeTmp.IsEnum(); // Note that the collectionItemTypeName will be null if the context does not have the ResolveName func. collectionItemTypeName = this.requestInfo.ResolveNameFromType(collectionItemType); if (enumerablePropertyValue != null) { collection.Items = Util.GetEnumerable( enumerablePropertyValue, (val) => { if (areEnumItems) { if (val == null) { return(new ODataEnumValue(null, collectionItemType.FullName) as ODataValue); } return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(val.ToString(), collectionItemTypeTmp), collectionItemType.FullName) as ODataValue); } else { WebUtil.ValidateCollectionItem(val); WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType); return(this.CreateODataComplexValue(collectionItemType, val, propertyName, true /*isCollectionItem*/, visitedComplexTypeObjects) as ODataValue); } }); } // TypeName for complex types needs to be the client type name (not the one we resolved above) since it will be looked up in the client model collectionTypeName = collectionItemType.FullName; } // Set the type name to use for client type lookups and validation. Because setting this value can cause validation to occur, we will // only do it for JSON Light, in order to avoid breaking changes with the WCF Data Services 5.0 release, since it was already shipped without this. if (!this.requestInfo.Format.UsingAtom) { collection.TypeName = GetCollectionName(collectionTypeName); } string wireTypeName = GetCollectionName(collectionItemTypeName); collection.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = wireTypeName }); return(collection); }
public void TypeNameShouldComeFromSerializationTypeNameAnnotationForCollectionValue() { var stna = new SerializationTypeNameAnnotation() {TypeName = "FromSTNA"}; var value = new ODataCollectionValue() {TypeName = "Collection(Edm.String)"}; value.SetAnnotation(stna); this.typeNameOracle.GetValueTypeNameForWriting(value, EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(true)), EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetString(false)), /* isOpenProperty*/ false).Should().Be("FromSTNA"); }