internal static void AddTypeNameAnnotationAsNeeded(ODataComplexValue 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 }); } }
public void TypeNameShouldComeFromSerializationTypeNameAnnotationForComplexValue() { var stna = new SerializationTypeNameAnnotation() {TypeName = "FromSTNA"}; var value = new ODataComplexValue() {TypeName = "Model.Bla"}; value.SetAnnotation(stna); this.typeNameOracle.GetValueTypeNameForWriting(value, new EdmComplexTypeReference(new EdmComplexType("Model", "Bla"), true), new EdmComplexTypeReference(new EdmComplexType("Model", "Bla"), false), /* isOpenProperty*/ false).Should().Be("FromSTNA"); }
public void ValueWithTypeNameAnnotationShouldReturnTypeNameFromAnnotation() { var complexValue = new ODataComplexValue() { TypeName = ComplexTypeName }; complexValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = "TypeNameFromSTNA" }); this.testSubject.GetValueTypeNameForWriting( complexValue, complexTypeReference, complexTypeReference, /*isOpen*/ false) .Should().Be("TypeNameFromSTNA"); }
public void CollectionValueSerializationTypeNameAnnotationTest() { #region test cases var testCases = new[] { new { TypeName = (string)null, SerializationTypeNameAnnotation = (SerializationTypeNameAnnotation)null, XmlTypeName = MissingTypeNameSentinelTextAtom, JsonTypeName = MissingTypeNameSentinelTextJson, JsonLightTypeName = MissingTypeNameSentinelTextJson, ExpectedExceptionInJsonLight = (object)ODataExpectedExceptions.ODataException("WriterValidationUtils_MissingTypeNameWithMetadata"), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), ExpectedExceptionInAtomForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), }, new { TypeName = EntityModelUtils.GetCollectionTypeName("Edm.Int32"), SerializationTypeNameAnnotation = (SerializationTypeNameAnnotation)null, XmlTypeName = "<typeName>" + EntityModelUtils.GetCollectionTypeName("Edm.Int32") + "</typeName>", JsonTypeName = "\"type\":\"" + EntityModelUtils.GetCollectionTypeName("Edm.Int32") + "\"", JsonLightTypeName = MissingTypeNameSentinelTextJson, ExpectedExceptionInJsonLight = new object(), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = new object(), ExpectedExceptionInAtomForResponse = new object(), }, new { TypeName = EntityModelUtils.GetCollectionTypeName("Edm.Int32"), SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = null }, XmlTypeName = MissingTypeNameSentinelTextAtom, JsonTypeName = MissingTypeNameSentinelTextJson, JsonLightTypeName = MissingTypeNameSentinelTextJson, ExpectedExceptionInJsonLight = new object(), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = new object(), ExpectedExceptionInAtomForResponse = new object(), }, new { TypeName = (string)null, SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = EntityModelUtils.GetCollectionTypeName("Edm.String") }, XmlTypeName = "<typeName>" + EntityModelUtils.GetCollectionTypeName("Edm.String") + "</typeName>", JsonTypeName = "\"type\":\"" + EntityModelUtils.GetCollectionTypeName("Edm.String") + "\"", JsonLightTypeName = "\"@odata.type\":\"#" + EntityModelUtils.GetCollectionTypeName("Edm.String") + "\"", ExpectedExceptionInJsonLight = (object)ODataExpectedExceptions.ODataException("WriterValidationUtils_MissingTypeNameWithMetadata"), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = (object)ODataExpectedExceptions.ODataException("ODataJsonLightValueSerializer_MissingTypeNameOnCollection"), ExpectedExceptionInAtomForResponse = new object(), }, new { TypeName = (string)null, SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = string.Empty }, XmlTypeName = "<typeName></typeName>", JsonTypeName = "\"type\":\"\"", JsonLightTypeName = "\"@odata.type\":\"\"", ExpectedExceptionInJsonLight = (object)ODataExpectedExceptions.ODataException("WriterValidationUtils_MissingTypeNameWithMetadata"), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), ExpectedExceptionInAtomForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), }, new { TypeName = (string)null, SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = "NonCollectionTypeName" }, XmlTypeName = "<typeName>NonCollectionTypeName</typeName>", JsonTypeName = "\"type\":\"NonCollectionTypeName\"", JsonLightTypeName = "\"@odata.type\":\"#NonCollectionTypeName\"", ExpectedExceptionInJsonLight = (object)ODataExpectedExceptions.ODataException("WriterValidationUtils_MissingTypeNameWithMetadata"), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = (object)ODataExpectedExceptions.ODataException("ODataJsonLightValueSerializer_MissingTypeNameOnCollection"), ExpectedExceptionInAtomForResponse = new object(), }, new { TypeName = EntityModelUtils.GetCollectionTypeName("Edm.Int32"), SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = EntityModelUtils.GetCollectionTypeName("Edm.String") }, XmlTypeName = "<typeName>" + EntityModelUtils.GetCollectionTypeName("Edm.String") + "</typeName>", JsonTypeName = "\"type\":\"" + EntityModelUtils.GetCollectionTypeName("Edm.String") + "\"", JsonLightTypeName = "\"@odata.type\":\"#" + EntityModelUtils.GetCollectionTypeName("Edm.String") + "\"", ExpectedExceptionInJsonLight = new object(), ExpectedExceptionInAtom = new object(), ExpectedExceptionInJsonLightForResponse = new object(), ExpectedExceptionInAtomForResponse = new object(), }, }; #endregion test cases var testDescriptors = testCases.Select(tc => { EdmModel model = new EdmModel(); var owningEntityType = new EdmEntityType("TestNS", "OwningEntityType"); owningEntityType.AddStructuralProperty("PropertyName", EdmCoreModel.GetCollection(EdmCoreModel.Instance.GetInt32(isNullable: false))); model.AddElement(owningEntityType); var container = new EdmEntityContainer("TestNS", "TestContainer"); model.AddElement(container); ODataComplexValue complexValue = new ODataComplexValue(); complexValue.TypeName = tc.TypeName; complexValue.Properties = new[] { new ODataProperty() { Name = "TestProperty", Value = "TestValue" } }; if (tc.SerializationTypeNameAnnotation != null) { complexValue.SetAnnotation(tc.SerializationTypeNameAnnotation); } ODataCollectionValue collection = new ODataCollectionValue(); collection.TypeName = tc.TypeName; if (tc.SerializationTypeNameAnnotation != null) { collection.SetAnnotation(tc.SerializationTypeNameAnnotation); } return new PayloadWriterTestDescriptor<ODataProperty>( this.Settings, new ODataProperty { Name = "PropertyName", Value = collection }, (testConfiguration) => { if (testConfiguration.Format == ODataFormat.Atom) { if (tc.ExpectedExceptionInAtom is Exception) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException = (Exception)tc.ExpectedExceptionInAtom }; } var exception = testConfiguration.IsRequest ? tc.ExpectedExceptionInAtom : tc.ExpectedExceptionInAtomForResponse; if (exception is ExpectedException) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException2 = (ExpectedException)exception }; } return new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { FragmentExtractor = (result) => { string typeName = (string)result.Attribute(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.AtomTypeAttributeName); return typeName == null ? MissingTypeNameSentinelXElement : new XElement("typeName", typeName); }, Xml = tc.XmlTypeName }; } if (testConfiguration.Format == ODataFormat.Json) { if (testConfiguration.Format == ODataFormat.Json) { if (tc.ExpectedExceptionInJsonLight is Exception) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException = (Exception)tc.ExpectedExceptionInJsonLight }; } var exception = testConfiguration.IsRequest ? tc.ExpectedExceptionInJsonLight : tc.ExpectedExceptionInJsonLightForResponse; if (exception is ExpectedException) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException2 = (ExpectedException)exception }; } } return new JsonWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { FragmentExtractor = (result) => { var topLevelJsonObject = JsonLightWriterUtils.TrimWhitespace(result).Object(); JsonProperty typeProperty = null; if (topLevelJsonObject != null) { typeProperty = topLevelJsonObject.Property(JsonLightConstants.ODataTypeAnnotationName); } return typeProperty == null ? MissingTypeNameSentinelJsonProperty : typeProperty.RemoveAllAnnotations(true); }, Json = tc.JsonLightTypeName }; } throw new NotSupportedException("Format " + testConfiguration.Format.GetType().Name + " is not supported."); }) { Model = model, PayloadEdmElementContainer = owningEntityType }; }); this.CombinatorialEngineProvider.RunCombinations( testDescriptors, this.WriterTestConfigurationProvider.ExplicitFormatConfigurationsWithIndent, (testDescriptor, testConfig) => { testConfig = testConfig.Clone(); testConfig.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); testDescriptor.RunTopLevelPropertyPayload(testConfig, baselineLogger: this.Logger); }); }
public void ComplexValueSerializationTypeNameAnnotationTest() { // TODO: Move this string to resources and localize it const string ODataJsonLightValueSerializer_MissingTypeNameOnComplex = "A type name was not provided for an instance of ODataComplexValue."; var testCases = new[] { new { TypeName = (string)null, SerializationTypeNameAnnotation = (SerializationTypeNameAnnotation)null, XmlTypeName = MissingTypeNameSentinelTextAtom, JsonLightTypeName = (string)null, ExpectedExceptionInJsonLight = (object)ODataExpectedExceptions.ODataException("ODataJsonLightValueSerializer_MissingTypeNameOnComplex"), ExpectedExceptionInAtom = (object) null, ExpectedExceptionInJsonLightForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), ExpectedExceptionInAtomForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty") }, new { TypeName = "TestNS.MyType", SerializationTypeNameAnnotation = (SerializationTypeNameAnnotation)null, XmlTypeName = "<typeName>TestNS.MyType</typeName>", JsonLightTypeName = MissingTypeNameSentinelTextJson, ExpectedExceptionInJsonLight = (object) null, ExpectedExceptionInAtom = (object) null, ExpectedExceptionInJsonLightForResponse = (object) null, ExpectedExceptionInAtomForResponse = (object) null, }, new { TypeName = "TestNS.MyType", SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = null }, XmlTypeName = MissingTypeNameSentinelTextAtom, JsonLightTypeName = MissingTypeNameSentinelTextJson, ExpectedExceptionInJsonLight = (object) null, ExpectedExceptionInAtom = (object) null, ExpectedExceptionInJsonLightForResponse = (object) null, ExpectedExceptionInAtomForResponse = (object) null, }, new { TypeName = (string)null, SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = "DifferentType" }, XmlTypeName = "<typeName>DifferentType</typeName>", JsonLightTypeName = (string) null, ExpectedExceptionInJsonLight = (object) new ODataException(ODataJsonLightValueSerializer_MissingTypeNameOnComplex), ExpectedExceptionInAtom = (object) null, ExpectedExceptionInJsonLightForResponse = (object) null, ExpectedExceptionInAtomForResponse = (object) null, }, new { TypeName = (string)null, SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = string.Empty }, XmlTypeName = "<typeName></typeName>", JsonLightTypeName = (string) null, ExpectedExceptionInJsonLight = (object)ODataExpectedExceptions.ODataException("ODataJsonLightValueSerializer_MissingTypeNameOnComplex"), ExpectedExceptionInAtom = (object) null, ExpectedExceptionInJsonLightForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), ExpectedExceptionInAtomForResponse = (object)ODataExpectedExceptions.ODataException("ODataContextUriBuilder_TypeNameMissingForProperty"), }, new { TypeName = "TestNS.MyType", SerializationTypeNameAnnotation = new SerializationTypeNameAnnotation() { TypeName = "DifferentType" }, XmlTypeName = "<typeName>DifferentType</typeName>", JsonLightTypeName = "\"" + JsonLightConstants.ODataPropertyAnnotationSeparator + JsonLightConstants.ODataTypeAnnotationName + "\":\"DifferentType\"", ExpectedExceptionInJsonLight = (object) null, ExpectedExceptionInAtom = (object) null, ExpectedExceptionInJsonLightForResponse = (object) null, ExpectedExceptionInAtomForResponse = (object) null, }, }; var testDescriptors = testCases.Select(tc => { EdmModel model = new EdmModel(); var complexType = new EdmComplexType("TestNS", "MyType"); complexType.AddStructuralProperty("TestProperty", EdmCoreModel.Instance.GetString(isNullable: true)); model.AddElement(complexType); var owningEntityType = new EdmEntityType("TestNS", "OwningEntityType"); owningEntityType.AddStructuralProperty("PropertyName", new EdmComplexTypeReference(complexType, isNullable: true)); model.AddElement(owningEntityType); var container = new EdmEntityContainer("TestNS", "TestContainer"); model.AddElement(container); ODataComplexValue complexValue = new ODataComplexValue(); complexValue.TypeName = tc.TypeName; complexValue.Properties = new[] { new ODataProperty() { Name = "TestProperty", Value = "TestValue"} }; if (tc.SerializationTypeNameAnnotation != null) { complexValue.SetAnnotation(tc.SerializationTypeNameAnnotation); } return new PayloadWriterTestDescriptor<ODataProperty>( this.Settings, new ODataProperty { Name = "PropertyName", Value = complexValue }, (testConfiguration) => { if (testConfiguration.Format == ODataFormat.Atom) { if (tc.ExpectedExceptionInAtom is Exception) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException = (Exception)tc.ExpectedExceptionInAtom }; } var exception = testConfiguration.IsRequest ? tc.ExpectedExceptionInAtom : tc.ExpectedExceptionInAtomForResponse; if (exception is ExpectedException) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException2 = (ExpectedException)exception }; } return new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { FragmentExtractor = (result) => { string typeName = (string)result.Attribute(TestAtomConstants.ODataMetadataXNamespace + TestAtomConstants.AtomTypeAttributeName); return typeName == null ? MissingTypeNameSentinelXElement : new XElement("typeName", typeName); }, Xml = tc.XmlTypeName }; } else if (testConfiguration.Format == ODataFormat.Json) { if (tc.ExpectedExceptionInJsonLight is Exception) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException = (Exception)tc.ExpectedExceptionInJsonLight }; } var exception = testConfiguration.IsRequest ? tc.ExpectedExceptionInJsonLight : tc.ExpectedExceptionInJsonLightForResponse; if (exception is ExpectedException) { return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException2 = (ExpectedException)exception }; } return new JsonWriterTestExpectedResults(this.Settings.ExpectedResultSettings) { FragmentExtractor = (result) => { var complexObject = JsonLightWriterUtils.TrimWhitespace(result).Object(); JsonProperty typeProperty = null; if (complexObject != null) { typeProperty = complexObject.Property(JsonLightConstants.ODataTypeAnnotationName); } return typeProperty == null ? MissingTypeNameSentinelJsonProperty : typeProperty.RemoveAllAnnotations(true); }, Json = tc.JsonLightTypeName }; } else { throw new NotSupportedException("Format " + testConfiguration.Format.GetType().Name + " is not supported."); } }) { Model = model, PayloadEdmElementContainer = owningEntityType }; }); this.CombinatorialEngineProvider.RunCombinations( testDescriptors, this.WriterTestConfigurationProvider.ExplicitFormatConfigurationsWithIndent, (testDescriptor, testConfiguration) => { testConfiguration = testConfiguration.Clone(); testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri); testDescriptor.RunTopLevelPropertyPayload(testConfiguration, baselineLogger: this.Logger); }); }
public void BuildPropertyContextUriForComplexPropertyValueWithNonNullAnnotation() { ODataComplexValue value = new ODataComplexValue { TypeName = "FQNS.FromObject" }; value.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = "FQNS.FromAnnotation" }); var contextUri = this.CreatePropertyContextUri(value); contextUri.OriginalString.Should().Be(BuildExpectedContextUri("#FQNS.FromAnnotation")); }
internal static IEdmValue CreateStructuredEdmValue(ODataComplexValue complexValue, IEdmComplexTypeReference complexType) { if (complexType != null) { object typeAnnotation = ReflectionUtils.CreateInstance( odataTypeAnnotationType, new Type[] { typeof(IEdmComplexTypeReference) }, complexType); complexValue.SetAnnotation(typeAnnotation); } return (IEdmValue)ReflectionUtils.CreateInstance( odataEdmStructuredValueType, new Type[] { typeof(ODataComplexValue) }, complexValue); }
/// <summary> /// Creates and returns an ODataComplexValue from the given value. /// </summary> /// <param name="complexType">The value type.</param> /// <param name="value">The complex 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="isCollectionItem">True, if the value is an item in a collection, false otherwise.</param> /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param> /// <returns>An ODataComplexValue representing the given value.</returns> internal ODataComplexValue CreateODataComplexValue(Type complexType, object value, string propertyName, bool isCollectionItem, HashSet<object> visitedComplexTypeObjects) { Debug.Assert(complexType != null, "complexType != null"); Debug.Assert(value != null || !isCollectionItem, "Collection items must not be null"); ClientEdmModel model = this.requestInfo.Model; ClientTypeAnnotation complexTypeAnnotation = model.GetClientTypeAnnotation(complexType); Debug.Assert(complexTypeAnnotation != null, "complexTypeAnnotation != null"); Debug.Assert(!complexTypeAnnotation.IsEntityType, "Unexpected entity"); // Handle null values for complex types by putting m:null="true" if (value == null) { Debug.Assert(!isCollectionItem, "Null collection items are not supported. Should have already been checked."); return null; } if (visitedComplexTypeObjects == null) { visitedComplexTypeObjects = new HashSet<object>(ReferenceEqualityComparer<object>.Instance); } else if (visitedComplexTypeObjects.Contains(value)) { if (propertyName != null) { throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInComplexTypes(propertyName)); } else { Debug.Assert(complexTypeAnnotation.ElementTypeName != null, "complexTypeAnnotation.ElementTypeName != null"); throw Error.InvalidOperation(Strings.Serializer_LoopsNotAllowedInNonPropertyComplexTypes(complexTypeAnnotation.ElementTypeName)); } } visitedComplexTypeObjects.Add(value); ODataComplexValue odataComplexValue = new ODataComplexValue(); // When TypeName is set, it causes validation to occur when ODataLib writes out the collection. Part of the validation ensures that all items // in the collection are exactly the same type, no derived types are allowed. In the released WCF Data Services 5.0 implementation, we don't set // TypeName here, so that validation does not occur, therefore we will set this value only for JSON Light, so we don't break existing code. if (!this.requestInfo.Format.UsingAtom) { odataComplexValue.TypeName = complexTypeAnnotation.ElementTypeName; } string serverTypeName = this.requestInfo.GetServerTypeName(complexTypeAnnotation); odataComplexValue.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName }); odataComplexValue.Properties = this.PopulateProperties(value, serverTypeName, complexTypeAnnotation.PropertiesToSerialize(), visitedComplexTypeObjects); visitedComplexTypeObjects.Remove(value); return odataComplexValue; }