diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 164a076b346..c284f19acd6 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 10.1.3 + 10.1.4-beta07 diff --git a/src/BootstrapBlazor/Converter/JsonFilterKeyValueActionConverter.cs b/src/BootstrapBlazor/Converter/JsonFilterKeyValueActionConverter.cs index 31c214f9da6..fea2215cc4c 100644 --- a/src/BootstrapBlazor/Converter/JsonFilterKeyValueActionConverter.cs +++ b/src/BootstrapBlazor/Converter/JsonFilterKeyValueActionConverter.cs @@ -42,11 +42,7 @@ public sealed class JsonFilterKeyValueActionConverter : JsonConverter(ref reader, options); - if (val != null) - { - ret.SearchModel = val; - } + ReadSearchModel(ref reader, ret, options); } else if (propertyName == "pageIndex") { @@ -229,8 +225,7 @@ public override void Write(Utf8JsonWriter writer, QueryPageOptions value, JsonSe } if (value.SearchModel != null) { - writer.WritePropertyName("searchModel"); - writer.WriteRawValue(JsonSerializer.Serialize(value.SearchModel, options)); + WriteSearchModel(writer, value.SearchModel, options); } if (value.PageIndex > 1) { @@ -302,4 +297,52 @@ public override void Write(Utf8JsonWriter writer, QueryPageOptions value, JsonSe } writer.WriteEndObject(); } + + private static void WriteSearchModel(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + writer.WriteStartObject("searchModel"); + writer.WriteString("type", value.GetType().AssemblyQualifiedName); + writer.WritePropertyName("value"); + writer.WriteRawValue(JsonSerializer.Serialize(value, options)); + writer.WriteEndObject(); + } + + private static void ReadSearchModel(ref Utf8JsonReader reader, QueryPageOptions value, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + if (propertyName == "type") + { + reader.Read(); + Type? type = TypeExtensions.GetSafeType(reader.GetString()); + + reader.Read(); + propertyName = reader.GetString(); + if (propertyName == "value") + { + reader.Read(); + if (type != null) + { + value.SearchModel = JsonSerializer.Deserialize(ref reader, type, options); + } + else + { + value.SearchModel = JsonSerializer.Deserialize(ref reader, options); + } + } + } + } + } + } + } } diff --git a/src/BootstrapBlazor/Extensions/TypeExtensions.cs b/src/BootstrapBlazor/Extensions/TypeExtensions.cs index 911f4658cd9..1e3cd853e53 100644 --- a/src/BootstrapBlazor/Extensions/TypeExtensions.cs +++ b/src/BootstrapBlazor/Extensions/TypeExtensions.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone @@ -64,4 +64,20 @@ void EnsureNoAuthenticationSchemeSpecified() public static string GetUniqueTypeName(this Type type) => type.IsCollectible ? $"{type.FullName}-{type.TypeHandle.Value}" : $"{type.FullName}"; + + + /// + /// 通过 typeName 参数安全获取 Type 实例 + /// + /// + /// + public static Type? GetSafeType(string? typeName) + { + Type? type = null; + if (!string.IsNullOrEmpty(typeName)) + { + type = Type.GetType(typeName, throwOnError: false); + } + return type; + } } diff --git a/test/UnitTest/Extensions/QueryPageOptionsExtensionsTest.cs b/test/UnitTest/Extensions/QueryPageOptionsExtensionsTest.cs index 2e4a961425a..6cec1d3537f 100644 --- a/test/UnitTest/Extensions/QueryPageOptionsExtensionsTest.cs +++ b/test/UnitTest/Extensions/QueryPageOptionsExtensionsTest.cs @@ -182,7 +182,7 @@ public async Task Serialize_Ok() IsTriggerByPagination = true, IsPage = true, IsVirtualScroll = true, - SearchModel = new { Name = "Test1", Count = 2 } + SearchModel = new Foo { Name = "Test1", Count = 2 } }; model.Filters.Add(cut.Instance); @@ -196,6 +196,11 @@ public async Task Serialize_Ok() var expected = JsonSerializer.Deserialize(payload); Assert.NotNull(expected); Assert.Equal("SearchText", expected.SearchText); + + var foo = expected.SearchModel as Foo; + Assert.NotNull(foo); + Assert.Equal("Test1", foo.Name); + Assert.Equal("Name1", expected.SortName); Assert.Equal(3, expected.StartIndex); Assert.Equal(4, expected.PageIndex); @@ -216,6 +221,73 @@ public async Task Serialize_Ok() Assert.Equal(2, expected.AdvancedSortList.Count); } + [Fact] + public void SearchModel_Serialize() + { + var model = new QueryPageOptions + { + SearchModel = new { Name = "Test1", Count = 2 } + }; + var payload = JsonSerializer.Serialize(model); + var expected = JsonSerializer.Deserialize(payload); + + Assert.NotNull(expected); + Assert.NotNull(expected.SearchModel); + } + + [Fact] + public void SearchModel_Serialize_TableSearchModel() + { + var model = new QueryPageOptions + { + SearchModel = new FooSearchModel { Name = "Test1" } + }; + var payload = JsonSerializer.Serialize(model); + var expected = JsonSerializer.Deserialize(payload); + + Assert.NotNull(expected); + Assert.NotNull(expected.SearchModel); + Assert.Equal("FooSearchModel", expected.SearchModel.GetType().Name); + } + + [Fact] + public void SearchModel_Serialize_NullType() + { + var model = new QueryPageOptions + { + SearchModel = new FooSearchModel { Name = "Test1" } + }; + var payload = JsonSerializer.Serialize(model); + + // 更改为错误的类型 + payload = payload.Replace("FooSearchModel", "FooSearchModel1"); + var expected = JsonSerializer.Deserialize(payload); + + Assert.NotNull(expected); + Assert.NotNull(expected.SearchModel); + Assert.Equal("JsonElement", expected.SearchModel.GetType().Name); + } + + private class FooSearchModel : ITableSearchModel + { + public string? Name { get; set; } + + public IEnumerable GetSearches() + { + var ret = new List(); + if (!string.IsNullOrEmpty(Name)) + { + ret.Add(new SearchFilterAction(nameof(Foo.Name), Name)); + } + return ret; + } + + public void Reset() + { + Name = null; + } + } + [Fact] public void SerializeFilterAction_Ok() {