diff --git a/TencentCloud.Tests/CommonClient.cs b/TencentCloud.Tests/CommonClient.cs index 4c97461809..cf0296eab8 100644 --- a/TencentCloud.Tests/CommonClient.cs +++ b/TencentCloud.Tests/CommonClient.cs @@ -162,5 +162,112 @@ private TencentCloud.Common.CommonClient CreateClient() return new TencentCloud.Common.CommonClient( "cvm", "2017-03-12", cred, "ap-guangzhou", clientProfile); } + + [JsonConverter(typeof(Converter))] + public class CustomSSEResponse : AbstractSSEModel + { + public string Response; + + private class Converter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, value); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + var resp = new CustomSSEResponse(); + var jObject = JObject.Load(reader); + resp.Response = jObject.ToString(); + return resp; + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(CustomSSEResponse); + } + } + } + + [TestMethod] + public void TestSSEStream() + { + var mod = "hunyuan"; + var ver = "2023-09-01"; + var act = "ChatCompletions"; + var region = ""; + var endpoint = "hunyuan.tencentcloudapi.com"; + + var cred = new Credential + { + SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"), + SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY"), + }; + + var hpf = new HttpProfile + { + ReqMethod = "POST", + Endpoint = endpoint, + }; + var cpf = new ClientProfile(ClientProfile.SIGN_TC3SHA256, hpf); + + var client = new TencentCloud.Common.CommonClient(mod, ver, cred, region, cpf); + var param = new Dictionary + { + { "Model", "hunyuan-standard" }, + { + "Messages", + new object[] { new Dictionary { { "Role", "user" }, { "Content", "hi" } } } + }, + { "Stream", true }, + }; + + var req = new CommonRequest(param); + var resp = client.CallAsync(req, act).Result; + Assert.IsTrue(resp.IsStream); + foreach (var e in resp) + Console.WriteLine(e.Data); + } + + [TestMethod] + public void TestSSENonStream() + { + var mod = "hunyuan"; + var ver = "2023-09-01"; + var act = "ChatCompletions"; + var region = ""; + var endpoint = "hunyuan.tencentcloudapi.com"; + + var cred = new Credential + { + SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"), + SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY"), + }; + + var hpf = new HttpProfile + { + ReqMethod = "POST", + Endpoint = endpoint, + }; + var cpf = new ClientProfile(ClientProfile.SIGN_TC3SHA256, hpf); + + var client = new TencentCloud.Common.CommonClient(mod, ver, cred, region, cpf); + var param = new Dictionary + { + { "Model", "hunyuan-standard" }, + { + "Messages", + new object[] { new Dictionary { { "Role", "user" }, { "Content", "hi" } } } + }, + { "Stream", false }, + }; + + var req = new CommonRequest(param); + var resp = client.CallAsync(req, act).Result; + Assert.IsFalse(resp.IsStream); + Console.WriteLine(resp.Response); + } } } \ No newline at end of file diff --git a/TencentCloud.Tests/SerializeTest.cs b/TencentCloud.Tests/SerializeTest.cs new file mode 100644 index 0000000000..c87e7ec02e --- /dev/null +++ b/TencentCloud.Tests/SerializeTest.cs @@ -0,0 +1,160 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using TencentCloud.Common; +using TencentCloud.Common.Profile; +using TencentCloud.Cvm.V20170312; +using TencentCloud.Cvm.V20170312.Models; +using TencentCloud.Sms.V20210111; +using TencentCloud.Sms.V20210111.Models; + +namespace TencentCloud.Tests +{ + [TestClass] + public class SerializeTest + { + public class CustomContractResolver : DefaultContractResolver + { + protected override string ResolvePropertyName(string propertyName) + { + return propertyName.ToLower(); + } + } + + [TestMethod] + public void TestSerializeUnaffectedByDefaultSettings() + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + ContractResolver = new CustomContractResolver(), + }; + + var req = new SendSmsRequest(); + req.PhoneNumberSet = new[] { "test-number" }; + req.SmsSdkAppId = "test-app-id"; + req.TemplateId = "test-template-id"; + req.ExtendCode = null; + + var s = AbstractModel.ToJsonString(req); + + Assert.IsTrue(s.Contains("PhoneNumberSet")); + Assert.IsTrue(s.Contains("SmsSdkAppId")); + Assert.IsTrue(s.Contains("TemplateId")); + Assert.IsFalse(s.Contains("ExtendCode")); + + Assert.IsFalse(s.Contains("phonenumberset")); + Assert.IsFalse(s.Contains("smssdkappid")); + Assert.IsFalse(s.Contains("templateid")); + Assert.IsFalse(s.Contains("extendcode")); + } + + [TestMethod] + public void TestCommonSerializeUnaffectedByDefaultSettings() + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + ContractResolver = new CustomContractResolver(), + }; + + var req = new CommonRequest("{\"FieldA\": 0,\"FieldB\": null}"); + var s = req.Serialize(); + + Assert.IsTrue(s.Contains("FieldA")); + Assert.IsFalse(s.Contains("fielda")); + + Assert.IsTrue(s.Contains("FieldB")); + Assert.IsFalse(s.Contains("fielda")); + } + + [TestMethod] + public void TestApiSerializeUnaffectedByDefaultSettings() + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + ContractResolver = new CustomContractResolver(), + }; + + var cred = new Credential + { + SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"), + SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY"), + }; + + var clientProfile = new ClientProfile(); + + var client = new SmsClient(cred, "ap-guangzhou", clientProfile); + + var req = new SendSmsRequest(); + req.PhoneNumberSet = new[] { "test-number" }; + req.SmsSdkAppId = "test-app-id"; + req.TemplateId = "test-template-id"; + + try + { + client.SendSmsSync(req); + } + catch (TencentCloudSDKException e) + { + if (e.ErrorCode != "UnauthorizedOperation.SmsSdkAppIdVerifyFail") + { + // unexpected error + throw; + } + } + } + + [TestMethod] + public void TestDeserializeUnaffectedByDefaultSettings() + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + MissingMemberHandling = MissingMemberHandling.Error, + }; + + var val = AbstractModel.FromJsonString( + "{" + + " \"RegionSet\":[{}, {}]," + + " \"UnknownField\":0" + + "}" + ); + Assert.IsTrue(val.RegionSet != null); + Assert.IsTrue(val.RegionSet.Length == 2); + Assert.IsTrue(val.TotalCount == null); + } + + [TestMethod] + public void TestApiDeserializeUnaffectedByDefaultSettings() + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + ContractResolver = new CustomContractResolver(), + }; + + var cred = new Credential + { + SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"), + SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY"), + }; + + var clientProfile = new ClientProfile(); + var client = new CvmClient(cred, "ap-guangzhou", clientProfile); + + var resp = client.DescribeRegionsSync(new DescribeRegionsRequest()); + Console.WriteLine(AbstractModel.ToJsonString(resp)); + Assert.IsTrue(resp.RegionSet != null); + Assert.IsTrue(resp.TotalCount != null); + } + + [TestMethod] + public void TestSerializeIgnoreNull() + { + var req = new SendSmsRequest(); + req.PhoneNumberSet = new[] { "test-number" }; + req.ExtendCode = null; + + var s = AbstractModel.ToJsonString(req); + + Assert.IsTrue(s.Contains("PhoneNumberSet")); + Assert.IsFalse(s.Contains("ExtendCode")); + } + } +} \ No newline at end of file diff --git a/TencentCloud/Common/AbstractClient.cs b/TencentCloud/Common/AbstractClient.cs index 00d0cb870e..a59a74dc26 100644 --- a/TencentCloud/Common/AbstractClient.cs +++ b/TencentCloud/Common/AbstractClient.cs @@ -15,7 +15,6 @@ * under the License. */ -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; @@ -23,6 +22,7 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; +using Newtonsoft.Json; using TencentCloud.Common.Http; using TencentCloud.Common.Profile; @@ -155,7 +155,7 @@ protected async Task InternalRequest(AbstractModel request, string actio JsonResponseModel errResp = null; try { - errResp = JsonConvert.DeserializeObject>(strResp); + errResp = AbstractModel.FromJsonString>(strResp); } catch (JsonSerializationException e) { @@ -251,7 +251,7 @@ private async Task ReadJsonResponseAsync(HttpResponseMessage response) JsonResponseModel errResp = null; try { - errResp = JsonConvert.DeserializeObject>(strResp); + errResp = AbstractModel.FromJsonString>(strResp); } catch (JsonSerializationException e) { @@ -264,7 +264,7 @@ private async Task ReadJsonResponseAsync(HttpResponseMessage response) errResp.Response.Error.Message, errResp.Response.Error.Code, errResp.Response.RequestId); } - return JsonConvert.DeserializeObject>(strResp).Response; + return AbstractModel.FromJsonString>(strResp).Response; } /// @@ -496,9 +496,7 @@ private byte[] BuildRequestPayload(AbstractModel request) } // Default: Serialize the request object to JSON - return Encoding.UTF8.GetBytes( - JsonConvert.SerializeObject(request, Formatting.None, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })); + return Encoding.UTF8.GetBytes(AbstractModel.ToJsonString(request)); } /// diff --git a/TencentCloud/Common/AbstractModel.cs b/TencentCloud/Common/AbstractModel.cs index 55dae27b51..772ee54e3d 100644 --- a/TencentCloud/Common/AbstractModel.cs +++ b/TencentCloud/Common/AbstractModel.cs @@ -18,6 +18,9 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; namespace TencentCloud.Common { @@ -111,26 +114,54 @@ protected void SetParamArrayObj(Dictionary map, String prefix } } + private static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + }); + + private static readonly JsonSerializer Deserializer = JsonSerializer.Create(); + /// /// Serializes an object of AbstractModel into a JSON string. /// - /// A class inherited from AbstractModel. - /// An object of the class. + /// A class inherited from AbstractModel. + /// An object of the class. /// JSON formatted string representing the object. - public static string ToJsonString(V obj) where V : AbstractModel + public static string ToJsonString(T value) where T : AbstractModel { - return JsonConvert.SerializeObject(obj); + return JsonSerialize(value); + } + + internal static string JsonSerialize(T value) + { + var sb = new StringBuilder(256); + var sw = new StringWriter(sb, CultureInfo.InvariantCulture); + using (var jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Serializer.Formatting; + Serializer.Serialize(jsonWriter, value, null); + } + + return sw.ToString(); } /// /// Deserializes a JSON formatted string into an object of a class inherited from AbstractModel. /// - /// A class inherited from AbstractModel. - /// The JSON formatted string to deserialize. + /// A class inherited from AbstractModel. + /// The JSON formatted string to deserialize. /// An object of the class. - public static V FromJsonString(string json) + public static T FromJsonString(string value) + { + return JsonDeserialize(value); + } + + internal static T JsonDeserialize(string value) { - return JsonConvert.DeserializeObject(json); + using (var reader = new JsonTextReader(new StringReader(value))) + { + return (T)Deserializer.Deserialize(reader, typeof(T)); + } } /// diff --git a/TencentCloud/Common/CommonRequest.cs b/TencentCloud/Common/CommonRequest.cs index abc501fa6e..7f1859c7ed 100644 --- a/TencentCloud/Common/CommonRequest.cs +++ b/TencentCloud/Common/CommonRequest.cs @@ -129,7 +129,7 @@ private void ToMapFromValue(Dictionary map, string prefix, JToke /// The JSON string representing the request body. public string Serialize() { - return JsonConvert.SerializeObject(_body); + return JsonSerialize(_body); } } } \ No newline at end of file