簡體   English   中英

從 C# 中的 Json 字符串中檢索特定類型的實際具體 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)......在我不使用的一種方法中知道要返回哪種類型,因為 object 是從 Json-String 反序列化的。 還把 T 和放在提供 object 支持的方法上,但它適用於這兩個類,這不是我想要的......我什至在每個不同的底層 Type 類中放置了一個枚舉,但自從類型也在基 class 中定義。

如何或什么是確定如何從 json 字符串中獲取實際 object 類型的最佳方法,其中有相似的構造函數,但它們仍然彼此不同......

請幫我解決這個問題:(

下面你會找到我想要實現的示例代碼..

就像我之前說的,我有一個基礎 class 或接口無關緊要,還有兩個子類

基地 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;
    }
}

孩子 Class:例如 ChildA.cs

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

    public ChildA()
    {

    }

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

    }
}

孩子 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 字符串,我想將其轉換為 ChildA 或 ChildB。 Json-String 是這樣的:

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

讓我們嘗試轉換不起作用的 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;
    }

這里的問題是我希望 ChildB 回饋。 但是,由於兩個類具有相同的構造函數。 它無法識別將 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 params 構造函數運行 2 params 構造函數設置FullName屬性。 如果你使用baseFullName將不會被初始化,你必須在你的 3 params 構造函數中復制這一行。

C# 知道任何 object 的類型。實際上,您不需要ClassType屬性。 您可以執行以下操作:

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

我建議您刪除此屬性。

此外,該財產受到保護。 您不能通過序列化更改它的值。 序列化(默認情況下)適用於公共屬性。

我認為,您問題的關鍵在於構造函數。 序列化時,始終使用無參數構造函數。 在反序列化中,調用不帶參數的構造函數,然后設置屬性。

永遠不會使用 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 (因為StringToObjectT類型),然后設置所有 JSON 字符串(公共)屬性。 因此,您的類型屬性不可能對選擇 object 的類型有用。 collection不是您的 object 的屬性:這就是為什么您的 object 沒有獲得這些 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"
}

然后,您以這種方式獲得有效的 object:

var obj = StringToObject<ChildB>(json);

如果您要使用不同的類型和 inheritance,那么您必須使用TypeNameHandling = TypeNameHandling.All 。我在鏈接https://stackoverflow.com/a/72270480/18452174之前評論過的所有內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM