简体   繁体   English

从 C# 中的 Json 字符串中检索特定类型的实际具体 Object 的最佳方法?

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

I'm currently struggling with determining which types a json-string deserializer gives back in C#. I've tried to make a base TypeWHATEVER class where TypeX and TypeY inherited from them, but still whenever I try to retrieve the actual object with that specific type, it doesn't work since both can create a new instance with the same constructor.我目前正在努力确定 json-string 反序列化器在 C# 中返回哪些类型。我试图制作一个基本 TypeWHATEVER class,其中 TypeX 和 TypeY 从它们继承,但仍然每当我尝试检索实际的 object 与特定类型,它不起作用,因为两者都可以使用相同的构造函数创建一个新实例。

Let's say I have TypeX and TypeY.假设我有 TypeX 和 TypeY。 They both have similar constructors, for example: both have a constructor with parameters (string a, string b).它们都有相似的构造函数,例如:都有一个带有参数(字符串a,字符串b)的构造函数。 However, TypeY has an additional constructor with parameters, for example (string a, string b, string c).但是,TypeY 有一个额外的带参数的构造函数,例如 (string a, string b, string c)。

Now whenever I use the additional constructor or even just the similar constructor in TypeY, it still doesn't produce the results I want (It still sees it as TypeX as well as TypeY)... In one of the methods I don't know which type to return, because the object is being deserialized from a Json-String.现在,每当我在 TypeY 中使用额外的构造函数或什至只是类似的构造函数时,它仍然不会产生我想要的结果(它仍然将它视为 TypeX 和 TypeY)......在我不使用的一种方法中知道要返回哪种类型,因为 object 是从 Json-String 反序列化的。 Also putting T and on the method that gives the object backs works, but it works for both classes which isn't what I want... I even put a enum in each different underlying Type classes, but that didn't work since the Type is defined in the base class as well.还把 T 和放在提供 object 支持的方法上,但它适用于这两个类,这不是我想要的......我什至在每个不同的底层 Type 类中放置了一个枚举,但自从类型也在基 class 中定义。

How or what is the best way to determine how to get the actual object type from a json string where there are similar constructors, but still they differ from each other...如何或什么是确定如何从 json 字符串中获取实际 object 类型的最佳方法,其中有相似的构造函数,但它们仍然彼此不同......

Please help me through this:(请帮我解决这个问题:(

Below you'll find sample code of what I'm trying to achieve..下面你会找到我想要实现的示例代码..

Like I said earlier I have a base class or interface doesn't matter and two child classes就像我之前说的,我有一个基础 class 或接口无关紧要,还有两个子类

Base Class: Eg Parent.cs基地 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: Eg ChildA.cs孩子 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: Eg ChildB.cs孩子 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 Converter Class: Eg JsonConverterClass.cs Json 转换器 Class:例如 JsonConverterClass.cs

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

Now I have a Json-String that I want to convert to either a ChildA or ChildB.现在我有一个 Json 字符串,我想将其转换为 ChildA 或 ChildB。 The Json-String is something like this: Json-String 是这样的:

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

Let's try to convert the Json-string which won't work and give that Child object back in a method, unfortunate this doesn't work:让我们尝试转换不起作用的 Json 字符串,并在方法中返回 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;
    }

The problem here is that I expect ChildB to given back.这里的问题是我希望 ChildB 回馈。 However, since both classes have the same constructor.但是,由于两个类具有相同的构造函数。 It doesn't recognizes to give ChildB back.它无法识别将 ChildB 还给用户。 In fact, it just gives a random class back.. Either ChildA or ChilB and in most cases it just gives ChildA back what's really weird.事实上,它只是返回一个随机的 class。ChildA 或 ChilB,在大多数情况下,它只是返回 ChildA 真正奇怪的东西。

Hopefully, I could be clear as possible to inform you about what is going on and I really don't know why my approach doesn't work...希望我能尽可能清楚地告知您正在发生的事情,我真的不知道为什么我的方法不起作用......

Ok, I think there are many things to comment.好的,我认为有很多事情要评论。 Let's begin:让我们开始:

In your constructor with 3 parameters, use this instead of base .在具有 3 个参数的构造函数中,使用this而不是base Imagine this situation:想象一下这种情况:

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)
{
}

Calling this , your 3 params constructor run the 2 params constructor setting the FullName property.调用this ,你的 3 params 构造函数运行 2 params 构造函数设置FullName属性。 If you use base , FullName won't be initialized and you must duplicate this line in your 3 params constructor.如果你使用baseFullName将不会被初始化,你必须在你的 3 params 构造函数中复制这一行。

C# know the type of any object. Really, you don't need your ClassType property. C# 知道任何 object 的类型。实际上,您不需要ClassType属性。 You can do things like:您可以执行以下操作:

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

I recomend you remove this property.我建议您删除此属性。

Also, that property is protected.此外,该财产受到保护。 You can't change it's value from serialization.您不能通过序列化更改它的值。 Serialization (by default) works with public properties.序列化(默认情况下)适用于公共属性。

The key in your question, I think, is with the constructors.我认为,您问题的关键在于构造函数。 When you serialize, you use always the parameterless constructor.序列化时,始终使用无参数构造函数。 In the deserialization, constructor without parameters is invoked and later, the properties are setted.在反序列化中,调用不带参数的构造函数,然后设置属性。

Your Type property in the JSON string is never used.永远不会使用 JSON 字符串中的Type属性。 You can't set the value because is protected and your instance is created before you start processing these properties.您无法设置该值,因为它受到保护并且您的实例是在您开始处理这些属性之前创建的。

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);

When you run the previous code, a ChildB object is created (because T type of StringToObject ) and then, all the JSON string (public) properties are setted.当您运行前面的代码时,会创建ChildB (因为StringToObjectT类型),然后设置所有 JSON 字符串(公共)属性。 So it's impossible that your type property may be useful to choose the type of the object. collection is not a property of your object: this is the reason why your object don't get these A,B,C values.因此,您的类型属性不可能对选择 object 的类型有用。 collection不是您的 object 的属性:这就是为什么您的 object 没有获得这些 A、B、C 值的原因。

With this sample:使用此示例:

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

Your JSON must be like this:您的 JSON 必须是这样的:

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

And then, you get your valid object in this way:然后,您以这种方式获得有效的 object:

var obj = StringToObject<ChildB>(json);

If you are going to work with distinct types and inheritance, then you must use the TypeNameHandling = TypeNameHandling.All that I commented before i my link https://stackoverflow.com/a/72270480/18452174 .如果您要使用不同的类型和 inheritance,那么您必须使用TypeNameHandling = TypeNameHandling.All 。我在链接https://stackoverflow.com/a/72270480/18452174之前评论过的所有内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM