将 JSON 反序列化为具有抽象和 non-abstract 类 的 object

Deserializing JSON into an object having abstract and non-abstract classes

我想将以下 JSON 反序列化到我的 C# 树 object 中。无法编辑树结构。 JSON:

{
  "Root": {
    "Type": 0,
    "children": [
      {
        "Type": 1,
        "Name": " SERVICES",
        "children": [
          {
            "Type": 2,
            "Name": " SERVICES",
            "Code": "S01",
            "children": [],
            "leaves": [
              {
                "Type": 6,
                "Code": "H-L-CWP-50",
                "Uom": "SQM",
                "Measurements": "SQM",
                "BaseRate": "€20"
              },
              {
                "Type": 6,
                "Code": "HMS-REM-001-03",
                "Uom": "SQ.M",
                "Measurements": "SQ.M",
                "BaseRate": "€6.38"
              }
            ]
          }
        ]
      }
    ]
  }
}

C# 树:

public class Tree
{
        public Root Root { get; set; }
        public Tree()
        {}
}

public class Root : Node
{
   public override NodeType Type => NodeType.Root;
}

public class Group : Node
{
        public override NodeType Type => NodeType.Group;
}

public class Category : Node
{
    public override NodeType Type => NodeType.Category;
}

public class Type : Node
{
    public override NodeType Type => NodeType.Type;
}

public class SubType : Node
{
    public override NodeType Type => NodeType.SubType;
}

public class SubSubType : Node
{
    public override NodeType Type => NodeType.SubsubType;
}
}

public abstract class Node
{
        public int? ID { get; set; }
        public int? FK { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public List<Node> children { get; set; }
        public List<Item> leaves { get; set; }
}

public class Item
{
        public string Code { get; set; }
        public string Desc { get; set; }
        public string Uom { get; set; }
        public float? Measurements { get; set; }
        public int? FK { get; set; }
}

并且树的设计方式是节点可以包含其他节点的列表以及项目列表; children 并分别在 JSON.

离开

使用我构建的自定义转换器,只要不存在叶子,我就可以反序列化节点,但是填充的叶子会使 JSON 无法识别并引发异常。

public class NodeConverter : JsonCreationConverter<Node> //notice the Node type here where in fact its a mixture of Nodes and Items.
    {
        protected override Node Create(Type objectType, JObject jObject)
        {
            switch ((Node.NodeType)jObject["Type"].Value<int>())
            {
                case Node.NodeType.Root:
                    return new Root();
                case Node.NodeType.Group:
                    return new Group();
                case Node.NodeType.Category:
                    return new Category();
                case Node.NodeType.Type:
                    return new Type();
                case Node.NodeType.SubType:
                    return new SubType();
                case Node.NodeType.SubsubType:
                    return new SubSubType();
                case Node.NodeType.Item: //I tried this but of course it won't work.
                    return new Item();
            }
            return null;
        }
    }

非常感谢任何 solutions/examples!!!

谢谢大家。

对于那些遇到同样问题的人,我已经通过组合不同的答案解决了我的问题。

类 项目和节点已使用 JSON 自定义转换器归因:

[JsonConverter(typeof(JSONTreeConverter))]
public abstract class Node

[JsonConverter(typeof(JSONTreeConverter))]
public class Item

这使每个 class 都可以在初始化之前使用自定义转换器。 然后自定义转换器 return 是一个通过反射转换为正确节点或项目类型的对象。

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    object targetObj = null;
    JObject jo = JObject.Load(reader);
    try
    {
        targetObj = Activator.CreateInstance(objectType); //instantiating concrete and known types
    }
    catch (Exception exc)
    {
        switch ((Node.NodeType)jo["Type"].Value<int>())
        {
            case Node.NodeType.Root:
                targetObj = new Root();
                break;
            case Node.NodeType.Group:
                targetObj = new Group();
                break;
            case Node.NodeType.Category:
                targetObj = new ValescoCategory();
                break;
            case Node.NodeType.Type:
                targetObj = new Type();
                break;
            case Node.NodeType.SubType:
                targetObj = new SubType();
                break;
            case Node.NodeType.SubsubType:
                targetObj = new SubSubType();
                break;
            case Node.NodeType.Item:
                targetObj = new Item(); //now this is possible ;)
                break;
        }
    }

    foreach (PropertyInfo prop in objectType.GetProperties().Where(p => p.CanRead && p.CanWrite))
    {
        JsonPropertyAttribute att = prop.GetCustomAttributes(true)
                                        .OfType<JsonPropertyAttribute>()
                                        .FirstOrDefault();

        string jsonPath = (att != null ? att.PropertyName : prop.Name);
        JToken token = jo.SelectToken(jsonPath);

        if (token != null && token.Type != JTokenType.Null)
        {
            object value = token.ToObject(prop.PropertyType, serializer);
            prop.SetValue(targetObj, value, null);
        }
    }

    return targetObj;
}

这样反序列化:JsonConvert.DeserializeObject<Tree>(tree);

就是这样! JSON 的另一个重写函数全部 return false,未实现 Write。

希望这对其他人有帮助,因为我花了 3 天才到达。