在 C# 中从 Json-String 检索特定类型的实际具体 Object 的最佳方法?

Best Way to Retrieve Actual Concrete Object Of a Certain Type From a Json-String in C#?

我目前正在努力确定 json-string 反序列化器在 C# 中返回哪些类型。我试图创建一个基础 TypeWHATEVER class ,其中 TypeX 和 TypeY 继承自它们,但每当我尝试检索具有该特定类型的实际 object 时,它都不起作用,因为两者都可以创建具有相同构造函数的新实例。

假设我有 TypeX 和 TypeY。它们都有相似的构造函数,例如:都有一个带有参数(字符串a,字符串b)的构造函数。但是TypeY多了一个带参数的构造函数,例如(string a, string b, string c).

现在每当我在 TypeY 中使用额外的构造函数或什至只是类似的构造函数时,它仍然不会产生我想要的结果(它仍然将它视为 TypeX 和 TypeY)... 在其中一种方法中,我不知道 return 是哪种类型,因为 object 是从 Json-String 反序列化的。同样将 T 和放在提供 object 支持的方法上,但它对两个 classes 都有效,这不是我想要的......我什至在每个不同的基础类型 classes,但这没有用,因为类型也在基础 class 中定义。

确定如何从 json 字符串中获取实际 object 类型的最佳方法或最佳方法是什么,其中有相似的构造函数,但它们仍然彼此不同...

请帮我解决这个问题:(

下面是我要实现的示例代码..

Like I said earlier I have a base class or interface doesn't matter and two child classes

基础Class:例如Parent.cs

public class Parent
{
    public virtual ClassType Type { get; protected set; } = ClassType.Undefined;//this is an enum and used to differentiate between the different classes. However, this doesn't work, because of the parent type will always be Undefined whenever a child type is converted from json-string to explicit Parent type...
    public string Id { get; set; }
    public string Message { get; set; }

    public Parent()
    {

    }

    public Parent(string id, string message = "")
    {
        Id = id;
        Message = message;
    }
}

Child Class:例如ChildA.cs

public class ChildA : Parent
{
    public override ClassType Type => ClassType.Default;

    public ChildA()
    {

    }

    public ChildA(string id, string message = "") : base(id, message)
    {

    }
}

Child Class:例如ChildB.cs

    public class ChildB : Parent
{
    private object testObjectA;
    private object testObjectB;
    private object testObjectC;

    public override ClassType Type => ClassType.New;

    public object TestObjectA
    {
        get { return testObjectA; }
        set { testObjectA = value; }
    }

    public object TestObjectB
    {
        get { return testObjectB; }
        set { testObjectB = value; }
    }

    public object TestObjectC
    {
        get { return testObjectC; }
        set { testObjectC = value; }
    }

    public ChildB()
    {

    }

    public ChildB(string id, string message) : base(id, message)
    {

    }

    public ChildB(string id, IDictionary<string, object> collection, string message = "") : this(id, message) // should I use 'this' or 'base' here for id and message, because I already get the base class for the second constructor and it makes sense to me just to take this class since the other is already been using the base class?
    {
        testObjectA = collection[Constants.A];
        testObjectB = collection[Constants.B];
        testObjectC = collection[Constants.C];
    }
}

Json转换器Class:例如JsonConverterClass.cs

public static T StringToObject<T>(string json)
        => JsonConvert.DeserializeObject<T>(json);//using Newtonsoft.Json;

现在我有一个 Json-String,我想将其转换为 ChildA 或 ChildB。 Json-String 是这样的:

{
  "type": "New",//This is the ClassType for example
  "id": "Some String...",
  "message": "",
  "collection": {
   "objectA": "A",
   "objectB": "B",
   "objectC": "C",
  }
}

让我们尝试转换不起作用的 Json-string 并将 Child object 返回到方法中,不幸的是这不起作用:

public Parent GetObjectExample(string json_String)
    {
        Parent parentClass = JsonConverterClass.StringToObject<Parent>(json_String);// I don't know how I maybe can use reflection here to accomplish, maybe this is an option too?

        switch (parentClass.Type)
        {
            case ClassType.Default:
                parentClass = parentClass as ChildA;
                break;
            case ClassType.New:
                parentClass = parentClass as ChildB;
                break;
        }

        return parentClass;
    }

这里的问题是我希望 ChildB 回馈。但是,由于两个 classes 具有相同的构造函数。它无法识别返回 ChildB。事实上,它只是随机返回 class.. ChildA 或 ChilB,在大多数情况下它只是返回 ChildA 真的很奇怪。

希望我能尽可能清楚地告知您正在发生的事情,我真的不知道为什么我的方法不起作用...

好的,我觉得有很多东西要评论。让我们开始吧:

在具有 3 个参数的构造函数中,使用 this 而不是 base。想象一下这种情况:

public ChildB(string id, string message) 
    : base(id, message)
{
    this.FullName = $"{id} {message}";
}

public ChildB(string id, IDictionary<string, object> collection, string message = "") 
    : this(id, message)
{
}

调用 this,您的 3 参数构造函数 运行 2 参数构造函数设置 FullName 属性。如果你使用 baseFullName 将不会被初始化,你必须在你的 3 参数构造函数中复制这一行。

C# 知道任何对象的类型。真的,您不需要 ClassType 属性。您可以执行以下操作:

if (myChildBInstance.GetType() == typeof(ChildB))

我建议你删除这个 属性。

此外,属性 受到保护。您不能通过序列化更改它的值。序列化(默认情况下)适用于 public 属性。

我认为你的问题的关键在于构造函数。序列化时,始终使用无参数构造函数。在反序列化中,调用不带参数的构造函数,然后设置属性。

您在 JSON 字符串中的 Type 属性 永远不会被使用。您无法设置该值,因为它受到保护并且您的实例是在您开始处理这些属性之前创建的。

var json = @"{
      'type': 'New',//This is the ClassType for example
      'id': 'Some String...',
      'message': '',
      'collection': {
       'objectA': 'A',
       'objectB': 'B',
       'objectC': 'C',
      }
    }";
var obj = StringToObject<ChildB>(json);

当你 运行 前面的代码时,会创建一个 ChildB 对象(因为 TStringToObject 的类型)然后,所有 JSON设置字符串 (public) 属性。所以你的类型 属性 不可能对选择对象的类型有用。 collection 不是您的对象的 属性:这就是您的对象没有获得这些 A、B、C 值的原因。

使用此示例:

var childB = new ChildB
{
    Id = "Id",
    Message = "Msg",
    TestObjectA = 1,
    TestObjectB = 2,
    TestObjectC = 3
};
var json = JsonConvert.SerializeObject(childB);

你的JSON一定是这样的:

{
   "TestObjectA":1,
   "TestObjectB":2,
   "TestObjectC":3,
   "Id":"Id",
   "Message":"Msg"
}

然后,您以这种方式获得有效对象:

var obj = StringToObject<ChildB>(json);

如果您要使用不同的类型和继承,那么您必须使用我之前评论过的 TypeNameHandling = TypeNameHandling.All 我的 link .