@@ -524,11 +524,13 @@ protected ODataJsonReaderNestedResourceInfo InnerReadUndeclaredProperty(IODataJs
524524 ValidateExpandedNestedResourceInfoPropertyValue ( this . JsonReader , isCollection , propertyName , payloadTypeReference ) ;
525525 if ( isCollection )
526526 {
527+ // Sam: Unclear that since it's an undeclared property, why call 'ReadExpandedResourceSetNestedResourceInfo' to tread it as navigation property, not call 'ReadNonExpandedResourceSetNestedResourceInfo'?
527528 readerNestedResourceInfo =
528- ReadExpandedResourceSetNestedResourceInfo ( resourceState , null , payloadTypeReference . ToStructuredType ( ) , propertyName , /* isDeltaResourceSet*/ false ) ;
529+ ReadExpandedResourceSetNestedResourceInfo ( resourceState , null , payloadTypeReference . ToStructuredType ( ) , propertyName , isDeltaResourceSet : false , this . MessageReaderSettings ) ;
529530 }
530531 else
531532 {
533+ // Sam: Unclear that since it's an undeclared property, why call 'ReadExpandedResourceNestedResourceInfo' to tread it as navigation property, not call 'ReadNonExpandedResourceNestedResourceInfo'?
532534 readerNestedResourceInfo = ReadExpandedResourceNestedResourceInfo ( resourceState , null , propertyName , payloadTypeReference . ToStructuredType ( ) , this . MessageReaderSettings ) ;
533535 }
534536
@@ -606,7 +608,7 @@ protected static void ValidateExpandedNestedResourceInfoPropertyValue(
606608 }
607609
608610 /// <summary>
609- /// Reads non-expanded nested resource set.
611+ /// Reads non-expanded (complex) nested resource set.
610612 /// </summary>
611613 /// <param name="resourceState">The state of the reader for resource to read.</param>
612614 /// <param name="collectionProperty">The collection of complex property for which to read the nested resource info. null for undeclared property.</param>
@@ -629,6 +631,8 @@ protected static ODataJsonReaderNestedResourceInfo ReadNonExpandedResourceSetNes
629631 IsComplex = true
630632 } ;
631633
634+ AttachPropertyAnnotationsToNestedResourceInfo ( resourceState , nestedResourceInfo ) ;
635+
632636 ODataResourceSet expandedResourceSet = CreateCollectionResourceSet ( resourceState , propertyName ) ;
633637 return ODataJsonReaderNestedResourceInfo . CreateResourceSetReaderNestedResourceInfo ( nestedResourceInfo , collectionProperty , nestedResourceType , expandedResourceSet ) ;
634638 }
@@ -656,13 +660,36 @@ protected static ODataJsonReaderNestedResourceInfo ReadNonExpandedResourceNested
656660 IsComplex = true
657661 } ;
658662
659- // Check the odata.type annotation for the complex property, it should show inside the complex object.
660- if ( ValidateDataPropertyTypeNameAnnotation ( resourceState . PropertyAndAnnotationCollector , nestedResourceInfo . Name ) != null )
663+ // Here's old logic: If a complex nested resource has `odata.type` property annotation, then throw exception, it says the annotation should be inside complex object.
664+ // It doesn't make sense now since the complex nested resoruce value could be null , in that case the `odata.type` annotation has to be outside the complex object.
665+ AttachPropertyAnnotationsToNestedResourceInfo ( resourceState , nestedResourceInfo ) ;
666+
667+ return ODataJsonReaderNestedResourceInfo . CreateResourceReaderNestedResourceInfo ( nestedResourceInfo , complexProperty , nestedResourceType ) ;
668+ }
669+
670+ protected static void AttachPropertyAnnotationsToNestedResourceInfo ( IODataJsonReaderResourceState resourceState , ODataNestedResourceInfo nestedResourceInfo )
671+ {
672+ Debug . Assert ( resourceState != null , "resourceState != null" ) ;
673+ Debug . Assert ( nestedResourceInfo != null , "nestedResourceInfo != null" ) ;
674+
675+ foreach ( KeyValuePair < string , object > odataAnnotation
676+ in resourceState . PropertyAndAnnotationCollector . GetODataPropertyAnnotations ( nestedResourceInfo . Name ) )
661677 {
662- throw new ODataException ( Error . Format ( SRResources . ODataJsonPropertyAndValueDeserializer_ComplexValueWithPropertyTypeAnnotation , ODataAnnotationNames . ODataType ) ) ;
678+ if ( string . Equals ( odataAnnotation . Key , "odata.type" , StringComparison . Ordinal )
679+ || string . Equals ( odataAnnotation . Key , "type" , StringComparison . Ordinal ) )
680+ {
681+ nestedResourceInfo . TypeAnnotation = new ODataTypeAnnotation ( ( string ) odataAnnotation . Value ) ;
682+ }
683+ else
684+ {
685+ nestedResourceInfo . InstanceAnnotations . Add ( new ODataInstanceAnnotation ( odataAnnotation . Key , odataAnnotation . Value . ToODataValue ( ) , true ) ) ;
686+ }
663687 }
664688
665- return ODataJsonReaderNestedResourceInfo . CreateResourceReaderNestedResourceInfo ( nestedResourceInfo , complexProperty , nestedResourceType ) ;
689+ foreach ( KeyValuePair < string , object > instanceAnnotation in resourceState . PropertyAndAnnotationCollector . GetCustomPropertyAnnotations ( nestedResourceInfo . Name ) )
690+ {
691+ nestedResourceInfo . InstanceAnnotations . Add ( new ODataInstanceAnnotation ( instanceAnnotation . Key , instanceAnnotation . Value . ToODataValue ( ) ) ) ;
692+ }
666693 }
667694
668695 /// <summary>
@@ -714,15 +741,27 @@ in resourceState.PropertyAndAnnotationCollector.GetODataPropertyAnnotations(nest
714741 break ;
715742
716743 default :
717- if ( messageReaderSettings . ThrowOnUndeclaredPropertyForNonOpenType )
744+ if ( messageReaderSettings . ThrowOnUnexpectedODataPropertyAnnotationOnNavigationProperty )
718745 {
719746 throw new ODataException ( Error . Format ( SRResources . ODataJsonResourceDeserializer_UnexpectedExpandedSingletonNavigationLinkPropertyAnnotation , nestedResourceInfo . Name , propertyAnnotation . Key ) ) ;
720747 }
721748
749+ // Let's save the property annotations into the nested resource info, therefore we can support the 'null' value nested resource as:
750+ // {
751+ // "[email protected] " : "anyvalue", 752+ // "NestedResource": null
753+ // }
754+ nestedResourceInfo . InstanceAnnotations . Add ( new ODataInstanceAnnotation ( propertyAnnotation . Key , propertyAnnotation . Value . ToODataValue ( ) , true ) ) ;
755+
722756 break ;
723757 }
724758 }
725759
760+ foreach ( KeyValuePair < string , object > instanceAnnotation in resourceState . PropertyAndAnnotationCollector . GetCustomPropertyAnnotations ( nestedResourceInfo . Name ) )
761+ {
762+ nestedResourceInfo . InstanceAnnotations . Add ( new ODataInstanceAnnotation ( instanceAnnotation . Key , instanceAnnotation . Value . ToODataValue ( ) ) ) ;
763+ }
764+
726765 return ODataJsonReaderNestedResourceInfo . CreateResourceReaderNestedResourceInfo ( nestedResourceInfo , navigationProperty , propertyType ) ;
727766 }
728767
@@ -734,11 +773,13 @@ in resourceState.PropertyAndAnnotationCollector.GetODataPropertyAnnotations(nest
734773 /// <param name="propertyType">The type of the collection.</param>
735774 /// <param name="propertyName">The property name.</param>
736775 /// <param name="isDeltaResourceSet">The property being read represents a nested delta resource set.</param>
776+ /// <param name="messageReaderSettings">The ODataMessageReaderSettings.</param>
737777 /// <returns>The nested resource info for the expanded link read.</returns>
738778 /// <remarks>
739779 /// This method doesn't move the reader.
740780 /// </remarks>
741- protected static ODataJsonReaderNestedResourceInfo ReadExpandedResourceSetNestedResourceInfo ( IODataJsonReaderResourceState resourceState , IEdmNavigationProperty navigationProperty , IEdmStructuredType propertyType , string propertyName , bool isDeltaResourceSet )
781+ protected static ODataJsonReaderNestedResourceInfo ReadExpandedResourceSetNestedResourceInfo ( IODataJsonReaderResourceState resourceState ,
782+ IEdmNavigationProperty navigationProperty , IEdmStructuredType propertyType , string propertyName , bool isDeltaResourceSet , ODataMessageReaderSettings messageReaderSettings )
742783 {
743784 Debug . Assert ( resourceState != null , "resourceState != null" ) ;
744785 Debug . Assert ( navigationProperty != null || propertyName != null , "navigationProperty != null || propertyName != null" ) ;
@@ -796,10 +837,21 @@ in resourceState.PropertyAndAnnotationCollector.GetODataPropertyAnnotations(nest
796837
797838 case ODataAnnotationNames . ODataDeltaLink : // Delta links are not supported on expanded resource sets.
798839 default :
799- throw new ODataException ( Error . Format ( SRResources . ODataJsonResourceDeserializer_UnexpectedExpandedCollectionNavigationLinkPropertyAnnotation , nestedResourceInfo . Name , propertyAnnotation . Key ) ) ;
840+ if ( messageReaderSettings . ThrowOnUnexpectedODataPropertyAnnotationOnNavigationProperty )
841+ {
842+ throw new ODataException ( Error . Format ( SRResources . ODataJsonResourceDeserializer_UnexpectedExpandedCollectionNavigationLinkPropertyAnnotation , nestedResourceInfo . Name , propertyAnnotation . Key ) ) ;
843+ }
844+
845+ nestedResourceInfo . InstanceAnnotations . Add ( new ODataInstanceAnnotation ( propertyAnnotation . Key , propertyAnnotation . Value . ToODataValue ( ) , true ) ) ;
846+ break ;
800847 }
801848 }
802849
850+ foreach ( KeyValuePair < string , object > instanceAnnotation in resourceState . PropertyAndAnnotationCollector . GetCustomPropertyAnnotations ( nestedResourceInfo . Name ) )
851+ {
852+ nestedResourceInfo . InstanceAnnotations . Add ( new ODataInstanceAnnotation ( instanceAnnotation . Key , instanceAnnotation . Value . ToODataValue ( ) ) ) ;
853+ }
854+
803855 return ODataJsonReaderNestedResourceInfo . CreateResourceSetReaderNestedResourceInfo ( nestedResourceInfo , navigationProperty , propertyType , expandedResourceSet ) ;
804856 }
805857
@@ -2338,7 +2390,7 @@ await ValidateExpandedNestedResourceInfoPropertyValueAsync(
23382390 if ( isCollection )
23392391 {
23402392 readerNestedResourceInfo =
2341- ReadExpandedResourceSetNestedResourceInfo ( resourceState , null , payloadTypeReference . ToStructuredType ( ) , propertyName , isDeltaResourceSet : false ) ;
2393+ ReadExpandedResourceSetNestedResourceInfo ( resourceState , null , payloadTypeReference . ToStructuredType ( ) , propertyName , isDeltaResourceSet : false , this . MessageReaderSettings ) ;
23422394 }
23432395 else
23442396 {
0 commit comments