我正在研究如下复杂的json struct ,其中"events"下的"id"代表

"events": [
{
    "id": 1,
    "actions": [
    {
        "id": 8,
        "values": {
            "obj_id":160,
            "url": "https://www.myurl.com/"
        }
    },
    {
        "id":15,
        "values":{
            "obj_id":182,
            "scale":200
        }
    }
    ]
}
]
    

我有一个抽象的MIDI Base类和多个派生类,不同的值基于不同的操作id.

public class Events
{
    public int id;
    public ActionsHolder[] actions;
}

[JsonConverter(typeof(BaseConverter))]
public class ActionsHolder
{
    public int id { get; set; } = -1;
    public ActionBase values { get; set; }
}

public abstract class ActionBase : ActionsHolder
{
    public int obj_id { get; set; }
}

public class OpenURLAction : ActionBase
{
    //Action id 8 = Open URL
    public string url { get; set; }
}

public class ScaleAction : ActionBase
{
    //Action id 15 = Scale
    public float scale { get; set; } = 1;
}
public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver
{
    protected override JsonConverter ResolveContractConverter(Type objectType)
    {
        if (typeof(ActionsHolder).IsAssignableFrom(objectType) && !objectType.IsAbstract)
        {
            return null; 
        }
            
        return base.ResolveContractConverter(objectType);
    }
}

public class BaseConverter : JsonConverter
{
    static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() 
    { 
        ContractResolver = new BaseSpecifiedConcreteClassConverter() ,
        TypeNameHandling = TypeNameHandling.All
    };

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(ActionsHolder));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject joAction = JObject.Load(reader);

        JObject joValues = (JObject)joAction["values"];
        switch (joAction["id"].Value<int>())
        {
            case 8:
                return new ActionsHolder
                {
                    id = joAction["id"].Value<int>(),
                    values = JsonConvert.DeserializeObject<OpenURLAction>(joValues.ToString(), SpecifiedSubclassConversion)
                };
            case 15:
                return new ActionsHolder
                {
                    id = joAction["id"].Value<int>(),
                    values = JsonConvert.DeserializeObject<ScaleAction>(joValues.ToString(), SpecifiedSubclassConversion)
                };
            default:
                return null;
        }
        throw new NotImplementedException();
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();returns false
    }
}

然而,它不起作用. 我的自定义转换器如何才能仅对其他人的动作属性和默认转换器进行反序列化. 有人可以帮忙吗?

推荐答案

我会修改您的类型层次 struct 和JsonConverter的设计,如下所示:

public class Root // Not shown in your question
{
    public List<Event> events { get; set; } = new();
}

public class Event
{
    public int id { get; set; } // Does this have a fixed value of 1?
    public List<ActionHolder> actions { get; set; } = new();
}

[JsonConverter(typeof(ActionHolderConverter))]
public class ActionHolder
{
    public int id => (int)(values?.id ?? ActionValueType.None);
    public ActionValueBase values { get; set; }
}

public enum ActionValueType
{
    None = -1,
    OpenURL = 8,
    Scale = 15,
}

public abstract class ActionValueBase
{
    [JsonIgnore] public abstract ActionValueType id { get; }
    public int obj_id { get; set; }
}

public class OpenURLActionValue : ActionValueBase
{
    public override ActionValueType id => ActionValueType.OpenURL;
    public string url { get; set; }
}

public class ScaleActionValue : ActionValueBase
{
    public override ActionValueType id => ActionValueType.Scale;
    public float scale { get; set; } = 1;
}

并将您的JsonConverter重写如下:

class ActionHolderConverter : JsonConverter<ActionHolder>
{
    public override ActionHolder ReadJson(JsonReader reader, Type objectType, ActionHolder existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        JObject joAction = JObject.Load(reader);
        
        var values = (ActionValueType)joAction[nameof(ActionHolder.id)].Value<int>() switch
        {
            ActionValueType.Scale => joAction[nameof(ActionHolder.values)]?.ToObject<ScaleActionValue>(serializer),
            ActionValueType.OpenURL => joAction[nameof(ActionHolder.values)]?.ToObject<OpenURLActionValue>(serializer),
            ActionValueType.None => (ActionValueBase)null,
            var t => throw new JsonSerializationException($"Unknown ActionValueTypes {t}"),
        };
        var holder = existingValue ?? new ActionHolder();
        holder.values = values;
        return holder;
    }

    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, ActionHolder value, JsonSerializer serializer) => throw new NotImplementedException();
}

要点:

  • 在您的设计中,ActionBase继承自ActionHolder,但这似乎是一个错误. 在您的SON中,具有两个值"id""values"events[*].actions[*]对象与具有"obj_id"属性和其他属性的嵌套"values"对象之间有明显的区别. 由于两者之间没有共同的属性,因此将ActionBase用作两者的Base是没有意义的.

  • 我引入了一个enum,对应于id的可能值.

  • 由于ActionHolder.id完全由值的类型定义,因此我将其设为只读并根据虚拟只读属性ActionValueBase确定,该属性再次根据类型确定.

  • 如果您有任何机会想要添加到您的eventsactions集合中,我建议使用可调整大小的List<T>列表,而不是固定长度的array.

演示小提琴here.

Csharp相关问答推荐

如何定义所有项目的解决方案版本?

在Dapper中使用IasyncEum重写GetAsyncEum方法

. NET WireMock拒绝PostAsJsonAsync序列化

`Task`只有在C#中等待时才会运行吗?

需要澄清C#的Clean Architecture解决方案模板的AuditableEntityInterceptor类

Blazor Foreach仅渲染最后一种 colored颜色

Azure DEVOPS找不到定制的Nuget包

Razor视图Razor页面指向同一端点时的优先级

用C#从Word文档中删除重复的节控件和文本内容控件

HttpClient 415不支持的媒体类型错误

try 在.Net核心身份注册页面中使用AJAX,但没有成功..NET Core 5.0 Razor页面应用程序

交替的奇数

如果是,我怎么才能让这个加75,如果不是,我怎么才能减go 100?

在implementationFactory中避免循环依赖

我什么时候应该在Dapper中使用Connection.OpenAsync?

在扩展方法中,IEnumerable<;T>;不会转换为IEumerable<;T&>

未在Windows上运行的Maui项目

如果所有";async任务方法()";调用都返回Task.FromResult()-是否同步执行?

使用生产环境调试我的应用程序的快速方法

在c#中,使用Okta和Blazor时,LocalReDirect()陷入循环,出现错误&请求太多.