diff --git a/src/Microsoft.OData.Core/Json/ODataJsonReader.cs b/src/Microsoft.OData.Core/Json/ODataJsonReader.cs
index 8094c7f7c7..93a9992e61 100644
--- a/src/Microsoft.OData.Core/Json/ODataJsonReader.cs
+++ b/src/Microsoft.OData.Core/Json/ODataJsonReader.cs
@@ -416,6 +416,17 @@ protected override bool ReadAtNestedPropertyInfoImplementation()
return this.ReadAtNestedPropertyInfoSynchronously();
}
+ ///
+ /// Asynchronous implementation of the reader logic when in state 'PropertyInfo'.
+ ///
+ /// A task that represents the asynchronous read operation.
+ /// The value of the TResult parameter contains true if more items can be read from the reader; otherwise false
+ ///
+ protected override Task ReadAtNestedPropertyInfoImplementationAsync()
+ {
+ return this.ReadAtNestedPropertyInfoAsynchronously();
+ }
+
#endregion
#region Stream
@@ -1537,6 +1548,54 @@ private bool ReadAtNestedPropertyInfoSynchronously()
return true;
}
+ ///
+ /// Asynchronous implementation of the reader logic when in state 'PropertyInfo'.
+ ///
+ ///
+ /// A task that represents the asynchronous read operation.
+ /// The value of the TResult parameter contains true if more items can be read from the reader; otherwise false
+ ///
+ ///
+ /// Pre-Condition: JsonNodeType.Property: there are more properties after the nested resource info in the owning resource
+ /// Post-Condition: JsonNodeType.StartObject start of the expanded resource nested resource info to read next
+ /// JsonNodeType.StartArray start of the expanded resource set nested resource info to read next
+ /// JsonNoteType.Primitive (null) expanded null resource nested resource info to read next
+ /// JsonNoteType.Property property after deferred link or entity reference link
+ /// JsonNodeType.EndObject end of the parent resource
+ ///
+ private async Task ReadAtNestedPropertyInfoAsynchronously()
+ {
+ Debug.Assert(this.CurrentScope.State == ODataReaderState.NestedProperty, "Must be on 'NestedProperty' scope.");
+ JsonNestedPropertyInfoScope nestedPropertyInfoScope = (JsonNestedPropertyInfoScope)this.CurrentScope;
+ ODataJsonReaderNestedPropertyInfo nestedPropertyInfo = nestedPropertyInfoScope.ReaderNestedPropertyInfo;
+ Debug.Assert(nestedPropertyInfo != null);
+
+ ODataPropertyInfo propertyInfo = this.CurrentScope.Item as ODataPropertyInfo;
+ Debug.Assert(propertyInfo != null, "Reading Nested Property Without an ODataPropertyInfo");
+
+ ODataStreamPropertyInfo streamPropertyInfo = propertyInfo as ODataStreamPropertyInfo;
+ if (streamPropertyInfo != null && !String.IsNullOrEmpty(streamPropertyInfo.ContentType))
+ {
+ this.StartNestedStreamInfo(new ODataJsonReaderStreamInfo(streamPropertyInfo.PrimitiveTypeKind, streamPropertyInfo.ContentType));
+ }
+ else if (!nestedPropertyInfo.WithValue)
+ {
+ // It's a nested non-stream property
+ this.PopScope(ODataReaderState.NestedProperty);
+
+ // We are reading a next nested info.
+ return await this.ReadNextNestedInfoAsync()
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ this.StartNestedStreamInfo(
+ new ODataJsonReaderStreamInfo(propertyInfo.PrimitiveTypeKind));
+ }
+
+ return true;
+ }
+
///
/// Implementation of the reader logic when in state 'Stream'.
///
diff --git a/test/FunctionalTests/Microsoft.OData.Core.Tests/Json/ODataJsonReaderTests.cs b/test/FunctionalTests/Microsoft.OData.Core.Tests/Json/ODataJsonReaderTests.cs
index 07bc6e19ea..fb3e9549e4 100644
--- a/test/FunctionalTests/Microsoft.OData.Core.Tests/Json/ODataJsonReaderTests.cs
+++ b/test/FunctionalTests/Microsoft.OData.Core.Tests/Json/ODataJsonReaderTests.cs
@@ -1641,6 +1641,112 @@ await SetupJsonReaderAndRunTestAsync(
Assert.Empty(verifyResourceActionStack);
}
+ [Fact]
+ public async Task ReadResourceWithNestedPropertyInfoAsync()
+ {
+ var payload = "{\"@odata.context\":\"http://tempuri.org/$metadata#Orders/$entity\"," +
+ "\"Id\":1," +
+ "\"Amount\":130," +
+ "\"ShippingAddress@odata.type\":\"#NS.Address\"}";
+
+ ODataResource orderResource = null;
+ ODataPropertyInfo shippingAddressPropertyInfo = null;
+
+ await SetupJsonReaderAndRunTestAsync(
+ payload,
+ this.orderEntitySet,
+ this.orderEntityType,
+ (jsonReader) => DoReadAsync(
+ jsonReader,
+ verifyResourceAction: (resource) =>
+ {
+ orderResource = resource;
+ },
+ verifyNestedPropertyInfoAction: (nestedPropertyInfo) =>
+ {
+ shippingAddressPropertyInfo = nestedPropertyInfo;
+ }));
+
+ Assert.NotNull(orderResource);
+ Assert.Equal("NS.Order", orderResource.TypeName);
+ Assert.Equal(2, orderResource.Properties.Count());
+
+ var idProperty = orderResource.Properties.ElementAt(0);
+ var amountProperty = orderResource.Properties.ElementAt(1);
+
+ Assert.Equal("Id", idProperty.Name);
+ Assert.Equal(1, idProperty.Value);
+ Assert.Equal("Amount", amountProperty.Name);
+ Assert.Equal(130M, amountProperty.Value);
+
+ Assert.NotNull(shippingAddressPropertyInfo);
+ Assert.Equal("ShippingAddress", shippingAddressPropertyInfo.Name);
+ }
+
+ [Fact]
+ public async Task ReadResourceWithStreamPropertyContentTypeUnspecifiedAsync()
+ {
+ var payload = "{\"@odata.context\":\"http://tempuri.org/$metadata#Customers/$entity\"," +
+ "\"Id\":1," +
+ "\"Name\":\"Sue\"," +
+ "\"Photo@odata.mediaEditLink\":\"http://tempuri.org/Customers(1)/Photo/Edit\"," +
+ "\"Photo@odata.mediaReadLink\":\"http://tempuri.org/Customers(1)/Photo\"," +
+ "\"Photo@odata.mediaEtag\":\"media-etag\"," +
+ "\"Photo\":\"AQIDBAUGBwgJAA==\"}";
+
+ ODataResource customerResource = null;
+ ODataPropertyInfo photoPropertyInfo = null;
+ byte[] photoBuffer = null;
+ int bytesRead = 0;
+
+ await SetupJsonReaderAndRunTestAsync(
+ payload,
+ this.customerEntitySet,
+ this.customerEntityType,
+ (jsonReader) => DoReadAsync(
+ jsonReader,
+ verifyResourceAction: (resource) =>
+ {
+ customerResource = resource;
+ },
+ verifyNestedPropertyInfoAction: (nestedPropertyInfo) =>
+ {
+ photoPropertyInfo = nestedPropertyInfo;
+ },
+ verifyBinaryStreamAction: async (binaryStream) =>
+ {
+ var maxLength = 10;
+ photoBuffer = new byte[maxLength];
+ bytesRead = await binaryStream.ReadAsync(photoBuffer, 0, maxLength);
+ }));
+
+ Assert.NotNull(customerResource);
+ Assert.Equal("NS.Customer", customerResource.TypeName);
+ Assert.Equal(3, customerResource.Properties.Count());
+
+ var idProperty = customerResource.Properties.ElementAt(0);
+ var nameProperty = customerResource.Properties.ElementAt(1);
+ var photoProperty = customerResource.Properties.ElementAt(2);
+
+ Assert.Equal("Id", idProperty.Name);
+ Assert.Equal(1, idProperty.Value);
+ Assert.Equal("Name", nameProperty.Name);
+ Assert.Equal("Sue", nameProperty.Value);
+ Assert.Equal("Photo", photoProperty.Name);
+
+ Assert.Equal(10, bytesRead);
+ Assert.NotNull(photoBuffer);
+ Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, photoBuffer);
+
+ Assert.NotNull(photoPropertyInfo);
+ var photoStreamInfoProperty = Assert.IsType(photoPropertyInfo);
+ Assert.Equal("Photo", photoStreamInfoProperty.Name);
+ Assert.Equal("http://tempuri.org/Customers(1)/Photo", photoStreamInfoProperty.ReadLink.OriginalString);
+ Assert.Equal("http://tempuri.org/Customers(1)/Photo/Edit", photoStreamInfoProperty.EditLink.OriginalString);
+ Assert.Null(photoStreamInfoProperty.ContentType);
+ Assert.Equal("media-etag", photoStreamInfoProperty.ETag);
+ }
+
[Fact]
public async Task ReadNestedResourceSetAsync_ThrowsExceptionForInvalidItemsInResourceSet()
{
@@ -1714,7 +1820,7 @@ private async Task DoReadAsync(
Action verifyDeletedResourceAction = null,
Action verifyDeltaLinkAction = null,
Action verifyEntityReferenceLinkAction = null,
- Action verifyNestedPropertyInfoAction = null,
+ Action verifyNestedPropertyInfoAction = null,
Action verifyBinaryStreamAction = null,
Action verifyTextStreamAction = null,
Action verifyPrimitiveAction = null)
@@ -1823,9 +1929,12 @@ private async Task DoReadAsync(
break;
case ODataReaderState.NestedProperty:
+ var nestedPropertyInfo = jsonReader.Item as ODataPropertyInfo;
+ Assert.NotNull(nestedPropertyInfo);
+
if (verifyNestedPropertyInfoAction != null)
{
- verifyNestedPropertyInfoAction(jsonReader.Item);
+ verifyNestedPropertyInfoAction(nestedPropertyInfo);
}
break;