diff --git a/sdk/core/Azure.Core/Azure.Core.sln b/sdk/core/Azure.Core/Azure.Core.sln index 4f42f4315f1e..1bf93f4cccfa 100644 --- a/sdk/core/Azure.Core/Azure.Core.sln +++ b/sdk/core/Azure.Core/Azure.Core.sln @@ -45,6 +45,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{13A0 samples\README.md = samples\README.md samples\RequestContext.md = samples\RequestContext.md samples\Response.md = samples\Response.md + samples\Serialization.md = samples\Serialization.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core.Expressions.DataFactory", "..\Azure.Core.Expressions.DataFactory\src\Azure.Core.Expressions.DataFactory.csproj", "{7723BA5F-0017-45B4-B584-7D11AA069735}" diff --git a/sdk/core/Azure.Core/samples/Serialization.md b/sdk/core/Azure.Core/samples/Serialization.md index d1b13ef92649..f25f19f9e01b 100644 --- a/sdk/core/Azure.Core/samples/Serialization.md +++ b/sdk/core/Azure.Core/samples/Serialization.md @@ -30,18 +30,29 @@ Deserialization ## Using explicit cast +When using protocol methods for advanced handling of RequestContext it is still possible to use the strongly typed models. +There is an explicit cast operator that can be used to convert the protocol Response to the strongly typed model. +There is also an explicit cast operator that can be used to convert the strongly typed model to the protocol RequestContent. + Serialization ```C# Snippet:ExplicitCast_Serialize -//TODO +PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); +DogListProperty dog = new DogListProperty("myPet"); +Response response = client.CreatePet("myPet", (RequestContent)dog); ``` Deserialization ```C# Snippet:ExplicitCast_Deserialize -//TODO +PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); +Response response = client.GetPet("myPet"); +DogListProperty dog = (DogListProperty)response; +Console.WriteLine(dog.IsHungry); ``` +Given that explicit cast does not allow for serialization options we might also consider a static `FromResponse` and instance `ToRequestContent` methods. + ## Using System.Text.Json Serialization diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs index 806daa772be8..1c69ba4fd24b 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/DogListProperty.cs @@ -28,6 +28,26 @@ public DogListProperty() { } + public static explicit operator DogListProperty(Response response) + { + using JsonDocument jsonDocument = JsonDocument.Parse(response.ContentStream); + var serializationOptions = new SerializableOptions() + { + IgnoreReadOnlyProperties = true, + IgnoreAdditionalProperties = true + }; + return DeserializeDogListProperty(jsonDocument.RootElement, serializationOptions); + } + + public static explicit operator RequestContent(DogListProperty dog) + { + var content = new Utf8JsonRequestContent(); + //content.JsonWriter.WriteObjectValue(dog); + //temp implementation due to IUtf8JsonSerializable signature mismatch since we added an options parameter + ((IUtf8JsonSerializable)dog).Write(content.JsonWriter, new SerializableOptions()); + return content; + } + #region Serialization void IUtf8JsonSerializable.Write(Utf8JsonWriter writer, SerializableOptions options) { diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/ExplicitCastTests.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/ExplicitCastTests.cs new file mode 100644 index 000000000000..a5a7376f2507 --- /dev/null +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/ExplicitCastTests.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Azure.Core.TestFramework; +using NUnit.Framework; + +namespace Azure.Core.Tests.ModelSerializationTests +{ + public class ExplicitCastTests + { + [Test] + public void CastFromResponse() + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.1,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + var stream = new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)); + stream.Position = 0; + Response response = new MockResponse(200); + response.ContentStream = stream; + DogListProperty dog = (DogListProperty)response; + + Assert.AreEqual("Doggo", dog.Name); + Assert.AreEqual(false, dog.IsHungry); + Assert.AreEqual(1.1, dog.Weight); + Assert.AreEqual(new List { "kibble", "egg", "peanut butter" }, dog.FoodConsumed); + Assert.AreEqual("Animalia", dog.LatinName); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.IsFalse(additionalProperties.ContainsKey("numberOfLegs")); //default cast doesn't include additional properties + } + + [Test] + public void CastToRequestContent() + { +#if NETFRAMEWORK + string requestContent = "{\"latinName\":\"Animalia\",\"name\":\"Doggo\",\"isHungry\":false,\"weight\":1.1000000000000001,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; +#else + string requestContent = "{\"latinName\":\"Animalia\",\"name\":\"Doggo\",\"isHungry\":false,\"weight\":1.1,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; +#endif + var dog = new DogListProperty + { + Name = "Doggo", + IsHungry = false, + Weight = 1.1, + FoodConsumed = new List { "kibble", "egg", "peanut butter" }, + }; + RequestContent content = (RequestContent)dog; + var stream = new MemoryStream(); + content.WriteTo(stream, default); + stream.Position = 0; + string contentString = new StreamReader(stream).ReadToEnd(); + Assert.AreEqual(requestContent, contentString); + } + } +} diff --git a/sdk/core/Azure.Core/tests/ModelSerializationTests/VerifyModels.cs b/sdk/core/Azure.Core/tests/ModelSerializationTests/VerifyModels.cs index adf79d0c733c..7811e13dc71b 100644 --- a/sdk/core/Azure.Core/tests/ModelSerializationTests/VerifyModels.cs +++ b/sdk/core/Azure.Core/tests/ModelSerializationTests/VerifyModels.cs @@ -21,7 +21,7 @@ private static void VerifyProperties(Animal x, Animal y, SerializableOptions opt Assert.That(x.Name, Is.EqualTo(y.Name)); Assert.That(x.Weight, Is.EqualTo(y.Weight)); - if (!options.IgnoreReadOnlyProperties) + if (!options.IgnoreAdditionalProperties) { var additionalPropertiesX = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(x) as Dictionary; var additionalPropertiesY = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(y) as Dictionary; diff --git a/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs b/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs index 6e98e17764de..5a41b6bbd9e4 100644 --- a/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs +++ b/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs @@ -95,6 +95,48 @@ public virtual Response GetPet(string id, RequestContext context = null) } } + /// Get a pet by its Id. + /// Id of pet to return. + /// The request context. +#pragma warning disable AZC0002 + public virtual async Task CreatePetAsync(string id, RequestContent content, RequestContext context = null) +#pragma warning restore AZC0002 + { + using var scope = _clientDiagnostics.CreateScope("PetStoreClient.GetPet"); + scope.Start(); + try + { + using HttpMessage message = CreateGetPetRequest(id, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Get a pet by its Id. + /// Id of pet to return. + /// The request context. +#pragma warning disable AZC0002 + public virtual Response CreatePet(string id, RequestContent content, RequestContext context = null) +#pragma warning restore AZC0002 + { + using var scope = _clientDiagnostics.CreateScope("PetStoreClient.GetPet"); + scope.Start(); + try + { + using HttpMessage message = CreateGetPetRequest(id, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + /// Create Request for and operations. /// Id of pet to return. /// The request context. diff --git a/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs b/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs index ed876b391499..222fd3580bd4 100644 --- a/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs +++ b/sdk/core/Azure.Core/tests/samples/SerializationSamples.cs @@ -6,6 +6,9 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Azure.Core.Experimental.Tests; +using Azure.Core.TestFramework; +using Azure.Core.Tests.ModelSerializationTests; using Azure.Identity; using Azure.Security.KeyVault.Secrets; using NUnit.Framework; @@ -55,7 +58,9 @@ public void NonTryDeserialize() public void ExplicitCastSerialize() { #region Snippet:ExplicitCast_Serialize - //TODO + PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); + DogListProperty dog = new DogListProperty("myPet"); + Response response = client.CreatePet("myPet", (RequestContent)dog); #endregion } @@ -64,7 +69,10 @@ public void ExplicitCastSerialize() public void ExplicitCastDeserialize() { #region Snippet:ExplicitCast_Deserialize - //TODO + PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); + Response response = client.GetPet("myPet"); + DogListProperty dog = (DogListProperty)response; + Console.WriteLine(dog.IsHungry); #endregion }