/// <summary> /// Reads a complex value. /// </summary> /// <param name="complexValueTypeReference">The expected 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> /// <param name="duplicatePropertyNamesChecker">The duplicate property names checker to use - this is always initialized as necessary, do not clear.</param> /// <returns>The value of the complex value.</returns> /// <remarks> /// Pre-Condition: JsonNodeType.Property - the first property of the complex value object, or the second one if the first one was odata.type. /// JsonNodeType.EndObject - the end object of the complex value object. /// Post-Condition: almost anything - the node after the complex value (after the EndObject) /// </remarks> private ODataComplexValue ReadComplexValue( IEdmComplexTypeReference complexValueTypeReference, string payloadTypeName, SerializationTypeNameAnnotation serializationTypeNameAnnotation, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker) { this.AssertJsonCondition(JsonNodeType.Property, JsonNodeType.EndObject); Debug.Assert(duplicatePropertyNamesChecker != null, "duplicatePropertyNamesChecker != null"); this.IncreaseRecursionDepth(); ODataComplexValue complexValue = new ODataComplexValue(); complexValue.TypeName = complexValueTypeReference != null ? complexValueTypeReference.FullName() : payloadTypeName; if (serializationTypeNameAnnotation != null) { complexValue.SetAnnotation(serializationTypeNameAnnotation); } if (complexValueTypeReference != null) { complexValue.SetAnnotation(new ODataTypeAnnotation(complexValueTypeReference)); } List<ODataProperty> properties = new List<ODataProperty>(); while (this.JsonReader.NodeType == JsonNodeType.Property) { this.ReadPropertyCustomAnnotationValue = this.ReadCustomInstanceAnnotationValue; this.ProcessProperty( duplicatePropertyNamesChecker, this.ReadTypePropertyAnnotationValue, (propertyParsingResult, propertyName) => { switch (propertyParsingResult) { case PropertyParsingResult.ODataInstanceAnnotation: if (string.CompareOrdinal(ODataAnnotationNames.ODataType, propertyName) == 0) { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_ComplexTypeAnnotationNotFirst); } else { throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedAnnotationProperties(propertyName)); } case PropertyParsingResult.CustomInstanceAnnotation: ODataAnnotationNames.ValidateIsCustomAnnotationName(propertyName); Debug.Assert( !this.MessageReaderSettings.ShouldSkipAnnotation(propertyName), "!this.MessageReaderSettings.ShouldReadAndValidateAnnotation(annotationName) -- otherwise we should have already skipped the custom annotation and won't see it here."); var customInstanceAnnotationValue = this.ReadCustomInstanceAnnotationValue(duplicatePropertyNamesChecker, propertyName); complexValue.InstanceAnnotations.Add(new ODataInstanceAnnotation(propertyName, customInstanceAnnotationValue.ToODataValue())); break; case PropertyParsingResult.PropertyWithoutValue: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_ComplexValuePropertyAnnotationWithoutProperty(propertyName)); case PropertyParsingResult.PropertyWithValue: // Any other property is data ODataProperty property = new ODataProperty(); property.Name = propertyName; // Lookup the property in metadata IEdmProperty edmProperty = null; bool ignoreProperty = false; if (complexValueTypeReference != null) { edmProperty = ReaderValidationUtils.ValidateValuePropertyDefined(propertyName, complexValueTypeReference.ComplexDefinition(), this.MessageReaderSettings, out ignoreProperty); } if (ignoreProperty && (this.JsonReader.NodeType == JsonNodeType.StartObject || this.JsonReader.NodeType == JsonNodeType.StartArray)) { this.JsonReader.SkipValue(); } else { // EdmLib bridge marks all key properties as non-nullable, but Astoria allows them to be nullable. // If the property has an annotation to ignore null values, we need to omit the property in requests. ODataNullValueBehaviorKind nullValueReadBehaviorKind = this.ReadingResponse || edmProperty == null ? ODataNullValueBehaviorKind.Default : this.Model.NullValueReadBehaviorKind(edmProperty); // Read the property value object propertyValue = this.ReadNonEntityValueImplementation( ValidateDataPropertyTypeNameAnnotation(duplicatePropertyNamesChecker, propertyName), edmProperty == null ? null : edmProperty.Type, /*duplicatePropertyNamesChecker*/ null, /*collectionValidator*/ null, nullValueReadBehaviorKind == ODataNullValueBehaviorKind.Default, /*isTopLevelPropertyValue*/ false, /*insideComplexValue*/ false, propertyName, edmProperty == null); if (nullValueReadBehaviorKind != ODataNullValueBehaviorKind.IgnoreValue || propertyValue != null) { duplicatePropertyNamesChecker.CheckForDuplicatePropertyNames(property); property.Value = propertyValue; var propertyAnnotations = duplicatePropertyNamesChecker.GetCustomPropertyAnnotations(propertyName); if (propertyAnnotations != null) { foreach (var annotation in propertyAnnotations) { if (annotation.Value != null) { // annotation.Value == null indicates that this annotation should be skipped. property.InstanceAnnotations.Add(new ODataInstanceAnnotation(annotation.Key, annotation.Value.ToODataValue())); } } } properties.Add(property); } } break; case PropertyParsingResult.EndOfObject: break; case PropertyParsingResult.MetadataReferenceProperty: throw new ODataException(ODataErrorStrings.ODataJsonLightPropertyAndValueDeserializer_UnexpectedMetadataReferenceProperty(propertyName)); } }); } Debug.Assert(this.JsonReader.NodeType == JsonNodeType.EndObject, "After all the properties of a complex value are read the EndObject node is expected."); this.JsonReader.ReadEndObject(); complexValue.Properties = new ReadOnlyEnumerable<ODataProperty>(properties); this.DecreaseRecursionDepth(); return complexValue; }