我在关注这个官方的post,也关注这个issue.

根据我读到的内容,我创建了我的PolymorphicTypInfoResolver,这与示例几乎完全相同.

public class NativePolymorphicTypInfoResolver : DefaultJsonTypeInfoResolver {
    public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) {

        JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);

        Type baseValueObjectType = typeof(ValueObject);
        if (jsonTypeInfo.Type == baseValueObjectType) {
            jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions {
                TypeDiscriminatorPropertyName = "$mytype",
                IgnoreUnrecognizedTypeDiscriminators = true,
                UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
                DerivedTypes = {}
            };
        }

        return jsonTypeInfo;
    }
}

然而,当涉及到DerivedTypes名成员时,我希望能够像这样动态设置

var types = Assembly
    .GetExecutingAssembly()
    .GetTypes()
    .Where(t => t.IsSubclassOf(typeof(MyBaseClass)))
    .ToArray();

然后设置数组,如果投影ToList(),则设置列表,如下所示

DerivedTypes = types.Select(t => new JsonDerivedType(t, t.Name.ToLower())).ToArray()

然而,我得到了一个语法错误,说明Property or indexer 'JsonPolymorphismOptions.DerivedTypes' cannot be assigned to -- it is read only是明确的.但我肯定错过了一些可能很愚蠢的东西...

有什么帮助吗?

推荐答案

编译错误是因为JsonPolymorphismOptions.DerivedTypes是一个预分配的IList<JsonDerivedType>类型的read-only collection property.因此,您不能将其赋给某些LINQ查询返回的值.

相反,您必须在构造后将派生类型添加到其中:

public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) {
    JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);

    Type baseValueObjectType = typeof(ValueObject);
    if (jsonTypeInfo.Type == baseValueObjectType) {
        jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions {
            TypeDiscriminatorPropertyName = "$mytype",
            IgnoreUnrecognizedTypeDiscriminators = true,
            UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
        };
        var types = Assembly
            .GetExecutingAssembly()
            .GetTypes()
            .Where(t => t.IsSubclassOf(typeof(ValueObject))); // Fixed MyBaseClass => ValueObject
        foreach (var t in types.Select(t => new JsonDerivedType(t, t.Name.ToLowerInvariant()))) // Fixed ToLower() => ToLowerInvariant
            jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(t);
    }

    return jsonTypeInfo;
}

here.第一次见面

请注意,从.NET8开始,我建议您使用JsonTypeInfo modifier而不是子类化DefaultJsonTypeInfoResolver,因为WithAddedModifier()可以处理包括source-generated contexts在内的任意IJsonTypeInfoResolver个实例.

为此,首先定义以下扩展方法:

public static class JsonExtensions
{
    public static void AddNativePolymorphicTypInfo(JsonTypeInfo jsonTypeInfo)
    {
        Type baseValueObjectType = typeof(ValueObject);
        if (jsonTypeInfo.Type == baseValueObjectType) {
            jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions {
                TypeDiscriminatorPropertyName = "$mytype",
                IgnoreUnrecognizedTypeDiscriminators = true,
                UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
            };
            var types = Assembly
                .GetExecutingAssembly()
                .GetTypes()
                .Where(t => t.IsSubclassOf(typeof(ValueObject))); // Fixed MyBaseClass => ValueObject
            foreach (var t in types.Select(t => new JsonDerivedType(t, t.Name.ToLowerInvariant()))) // Fixed ToLower() => ToLowerInvariant
                jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(t);
        }
    }
}

然后设置选项,例如,如下所示:

var options = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver() // Or use some source-generated context if you prefer
        .WithAddedModifier(JsonExtensions.AddNativePolymorphicTypInfo),
    // Add other options as required, e.g.
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};

var json = JsonSerializer.Serialize(rootObject, options);

演示小提琴#2here.

node :

  • 在创建JSON约定时,请始终确保大写或小写invariant locale个元数据名称.这确保了合同在所有地区都是相同的,并避免了例如Turkish I problem.

  • 我不得不用.Where(t => t.IsSubclassOf(typeof(ValueObject)))替换Where(t => t.IsSubclassOf(typeof(MyBaseClass))),才能使我的代码演示起作用.

  • DerivedTypes = {}编译,因为它使用read-only collection property initializer syntax. 当被初始化的属性是一个预分配的集合,并且值是出现在大括号{}中的一系列对象时,此语法自动调用Add(). 不幸的是,这种语法不能用于使用LINQ查询的结果自动填充集合,因此需要添加更传统的语法.


Update

在您的modified fiddle from comments中,您希望在序列化声明为ValueObject的子类型的值时,使用上面的AddNativePolymorphicTypInfo()版本自动添加多态类型鉴别器.在您的问题所示的代码中,您可以通过以下判断来防止出现这种情况:

if (jsonTypeInfo.Type == baseValueObjectType)

你需要判断一下

typeof(ValueObject).IsAssignableFrom(jsonTypeInfo.Type)

如果要将多态鉴别器自动添加到ValueObject的所有派生类型,即使在将其显式序列化为派生类型时也是如此,则必须按如下方式修改AddNativePolymorphicTypInfo():

public static class JsonExtensions
{
    public static void AddNativePolymorphicTypInfo(JsonTypeInfo jsonTypeInfo)
    {
        if (typeof(ValueObject).IsAssignableFrom(jsonTypeInfo.Type) && !jsonTypeInfo.Type.IsSealed) {
            jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions {
                TypeDiscriminatorPropertyName = "$mytype",
                IgnoreUnrecognizedTypeDiscriminators = true,
                UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
            };
            // This query is likely to be slow so you might want to cache all types assignable to typeof(ValueObject) in a lazy static singleton
            var types = Assembly
                .GetExecutingAssembly()
                .GetTypes()
                .Where(t => !t.IsAbstract && jsonTypeInfo.Type.IsAssignableFrom(t));
            foreach (var t in types.Select(t => new JsonDerivedType(t, t.Name.ToLowerInvariant()))) // Fixed ToLower() => ToLowerInvariant
                jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(t);
        }
    }
}

但是,请注意,在序列化声明为sealed个类型的值时,System.Text.Json不允许发出多态类型标识符.如果您的任何派生ValueObject类型是密封的,则必须使用不同的方法,例如将鉴别器作为合成属性插入.

here. history of life

Csharp相关问答推荐

在实际上是List T的 IESEARCH上多次调用First()是否不好?

Unity中的刚体2D运动

Blazor-从数据库内部服务器提取错误

模型绑定RazorPage表单

最新的Mediatr和具有同步方法的处理程序Handle:并非所有代码路径都返回值"

如何使用自定义负载均衡器管理Ocelot负载均衡器中的多线程和批读取

如何在C#中将方法/线程启动传递给基本构造函数

在swagger示例中添加默认数组列表

为什么方法的值在SELECT方法中不会更改?

C#自定义验证属性未触发IsValid方法

当try 测试具有协变返回类型的抽象属性时,类似功能引发System.ArgumentException

当我try 在与XAMP的MySQL服务器连接的ASP.NET核心应用程序中添加迁移时出现错误

如何从Entity Framework Core中填充ListIInterface

Visual Studio,Docker容器-容器调用:连接被拒绝

为什么我的自定义Json.net转换器不工作?

从MudAutoComplete打开对话框,列表仍然可见

Xamarin中出错.表单:应用程序的分部声明不能指定不同的基类

将列表转换为带有逗号分隔字符串形式的值的字典

如何更改Datagridview行标题

C#-如何将int引用获取到byte[]