How do I deserialize each element of a json array into their proper subclasses based on one of the element's values? (C#)

huangapple go评论73阅读模式
英文:

How do I deserialize each element of a json array into their proper subclasses based on one of the element's values? (C#)

问题

这是您提供的代码的翻译部分:

这是您提供的代码的翻译部分:

cs脚本 `Test`:

```csharp
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Linq;
using UnityEngine;
using System.IO;

public abstract class Test {
    public static Dictionary<string, Test> tests = JsonToTests();

    public string type { get; }
    public string stringId { get; }
    public string[] array { get; }
    public byte ebyte { get; }
    public short eshort { get; }
    public bool ebool { get; }
    public (byte, byte) tuple { get; }

    public static Dictionary<string, Test> JsonToTests() {
        string json = File.ReadAllText(Application.dataPath + "/test.json");
        JArray jsonArray = JArray.Parse(json);
        JObject[] jsonObjects = jsonArray.Cast<JObject>().ToArray();

        Dictionary<string, Test> returnData = new Dictionary<string, Test>();
        foreach (JObject jsonObject in jsonObjects) {
            string id   = jsonObject.Value<string>("stringId");
            Debug.Log("id: " + id);
            string type = jsonObject.Value<string>("type");
            Test test = null;

            switch (type) {
                case "plus":
                    test = jsonObject.ToObject<TestPlus>();
                break;
                case "plusplus":
                    test = jsonObject.ToObject<TestPlusPlus>();
                break;
                case "plusplusplus":
                    test = jsonObject.ToObject<TestPlusPlusPlus>();
                break;
            }

            if (test == null) {
                Debug.LogError("json 无效或其他问题");
            } else {
                returnData.Add(id, test);
            }
        }
        return returnData;
    }
}

public class TestPlus : Test {
    public string valuePlus { get; }
}

public class TestPlusPlus : Test {
    public string valuePlusPlus { get; }
}

public class TestPlusPlusPlus : Test {
    public string valuePlusPlusPlus { get; }
}

json脚本 test

[
  {
    "type": "plus",
    "stringId": "string!",
    "array":  [ 1, 1, 1],
    "ebyte":   255,
    "eshort":  256,
    "ebool":   true,
    "tuple": [ 2, 2 ],
    "valuePlus": "hello"
  },
  {
    "type": "plusplus",
    "stringId": "string!2",
    "array":  [ 1, 1, 1],
    "ebyte":   255,
    "eshort":  256,
    "ebool":   true,
    "tuple": [ 2, 2 ],
    "valuePlusPlus": "string!!!!!1"
  },
  {
    "type": "plusplusplus",
    "stringId": "string!3",
    "array":  [ 1, 1, 1],
    "ebyte":   255,
    "eshort":  256,
    "ebool":   true,
    "tuple": [ 2, 2 ],
    "valuePlusPlusPlus": "string!!!!!2"
  }
]

cs脚本 BasicScript

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasicScript : MonoBehaviour {
    void Update() {
        Debug.Log((Test.tests["string!"] as TestPlus).valuePlus);
        Debug.Log((Test.tests["string!2"] as TestPlusPlus).valuePlusPlus);
        Debug.Log((Test.tests["string!3"] as TestPlusPlusPlus).valuePlusPlusPlus);
    }
}

在运行 BasicScript 时,每帧都会得到3个null值,而不是预期的值。不过,Test 脚本的日志输出是预期的值。我知道,或者我非常确定,我可以像获取那些值一样获取它们的值,但我更喜欢如果它可以动态地添加我放入其中的新字段。


<details>
<summary>英文:</summary>
Is this even possible? I&#39;ve been trying for quite a while and can&#39;t figure it out, and can&#39;t really find any videos or anything on how to do it.
Here&#39;s the scripts I used:
cs script `Test`:

using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Linq;
using UnityEngine;
using System.IO;

public abstract class Test {
public static Dictionary<string, Test> tests = JsonToTests();

public string type { get; }
public string stringId { get; }
public string[] array { get; }
public byte ebyte { get; }
public short eshort { get; }
public bool ebool { get; }
public (byte, byte) tuple { get; }
public static Dictionary&lt;string, Test&gt; JsonToTests() {
string json = File.ReadAllText(Application.dataPath + &quot;/test.json&quot;);
JArray jsonArray = JArray.Parse(json);
JObject[] jsonObjects = jsonArray.Cast&lt;JObject&gt;().ToArray();
Dictionary&lt;string, Test&gt; returnData = new Dictionary&lt;string, Test&gt;();
foreach (JObject jsonObject in jsonObjects) {
string id   = jsonObject.Value&lt;string&gt;(&quot;stringId&quot;);
Debug.Log(&quot;id: &quot; + id);
string type = jsonObject.Value&lt;string&gt;(&quot;type&quot;);
Test test = null;
switch (type) {
case &quot;plus&quot;:
test = jsonObject.ToObject&lt;TestPlus&gt;();
break;
case &quot;plusplus&quot;:
test = jsonObject.ToObject&lt;TestPlusPlus&gt;();
break;
case &quot;plusplusplus&quot;:
test = jsonObject.ToObject&lt;TestPlusPlusPlus&gt;();
break;
}
if (test == null) {
Debug.LogError(&quot;the json is invalid or something&quot;);
} else {
returnData.Add(id, test);
}
}
return returnData;
}

}

public class TestPlus : Test {
public string valuePlus { get; }
}

public class TestPlusPlus : Test {
public string valuePlusPlus { get; }
}

public class TestPlusPlusPlus : Test {
public string valuePlusPlusPlus { get; }
}

json script `test`:

[
{
"type": "plus",
"stringId": "string!",
"array": [ 1, 1, 1],
"ebyte": 255,
"eshort": 256,
"ebool": true,
"tuple": [ 2, 2 ],
"valuePlus": "hello"
},
{
"type": "plusplus",
"stringId": "string!2",
"array": [ 1, 1, 1],
"ebyte": 255,
"eshort": 256,
"ebool": true,
"tuple": [ 2, 2 ],
"valuePlusPlus": "string!!!!!1"
},
{
"type": "plusplusplus",
"stringId": "string!3",
"array": [ 1, 1, 1],
"ebyte": 255,
"eshort": 256,
"ebool": true,
"tuple": [ 2, 2 ],
"valuePlusPlusPlus": "string!!!!!2"
}
]

cs script `BasicScript`:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasicScript : MonoBehaviour {
void Update() {
Debug.Log( (Test.tests["string!"] as TestPlus).valuePlus);
Debug.Log( (Test.tests["string!2"] as TestPlusPlus).valuePlusPlus);
Debug.Log((Test.tests["string!3"] as TestPlusPlusPlus).valuePlusPlusPlus);
}
}

While running `BasicScript` all I get is 3 null values every frame, instead of the expected ones.
The logs from the `Test` script however, are the expected values. I know—or am pretty sure—I can just get the values like I did those ones, but I&#39;d prefer if it dynamically? added new fields I put into them.
</details>
# 答案1
**得分**: 2
以下是您提供的代码的中文翻译部分:
无法反序列化为C#类,如果类不允许设置属性,则无法反序列化。您至少需要使用`init`。此外,元组也无法反序列化。修复后,此代码将正常工作。

List tests = JArray.Parse(json).Select(ja => (Test)(((string)ja["type"]) == "plus"
? ja.ToObject()
: (string)ja["type"] == "plusplus"
? ja.ToObject()
: ja.ToObject())).ToList();

public abstract class Test
{
public string type { get; init; }
public string stringId { get; init; }
public string[] array { get; init; }
public byte ebyte { get; init; }
public short eshort { get; init; }
public bool ebool { get; init; }
public byte[] tuple { get; init; }
}

public class TestPlus : Test
{
public string valuePlus { get; init; }
}

public class TestPlusPlus : Test
{
public string valuePlusPlus { get; init; }
}

public class TestPlusPlusPlus : Test
{
public string valuePlusPlusPlus { get; init; }
}


更新
`init` 只适用于C# 9+。否则,您可以添加`[JsonProperty]`属性和`private set`,例如:

public abstract class Test
{
[JsonProperty("type")]
public string type { get; private set; }

[JsonProperty("stringId")]
public string stringId { get; private set; }
// ... 其他属性

}


但如果您更改类名称或只更改JSON字符串的“type”属性以匹配类名称,代码将更简单:

List tests = JArray.Parse(json)
.Select(ja => (Test)ja.ToObject(Type
.GetType((string)ja["type"])))
.ToList();

public abstract class Test
{
[JsonProperty("type")]
public string type { get; private set; }

// 或者可能更好
//public string type { get { return this.GetType().Name; } }
[JsonProperty("stringId")]
public string stringId { get; private set; }
public string[] array { get; init; }
public byte ebyte { get; init; }
public short eshort { get; init; }
public bool ebool { get; init; }
public byte[] tuple { get; init; }

}

public class plus : Test
{
public string valuePlus { get; init; }
}

public class plusplus : Test
{
public string valuePlusPlus { get; init; }
}

public class plusplusplus : Test
{
public string valuePlusPlusPlus { get; init; }
}


<details>
<summary>英文:</summary>
You can not deserialize to c# class if class doesn&#39;t allow to set properties. You have to use init at least. Also tuple is not deserialized too. After fixing, this code works

List<Test> tests = JArray.Parse(json).Select(ja => (Test) (((string) ja["type"]) =="plus"
? ja.ToObject<TestPlus>()
: (string) ja["type"]=="plusplus"
? ja.ToObject<TestPlusPlus>()
: ja.ToObject<TestPlusPlusPlus>())).ToList();

public abstract class Test
{
public string type { get; init;}
public string stringId { get; init; }
public string[] array { get; init;}
public byte ebyte { get; init;}
public short eshort { get; init; }
public bool ebool { get; init;}
public byte[] tuple { get; init; }
}

public class TestPlus : Test
{
public string valuePlus { get; init;}
}

public class TestPlusPlus : Test
{
public string valuePlusPlus { get; init;}
}

public class TestPlusPlusPlus : Test
{
public string valuePlusPlusPlus { get; init;}
}

UPDATE 
init works only with c# 9+. Otherwise you can add [JsonProperty] attribute and private set for example

public abstract class Test
{
[JsonProperty("type")]
public string type { get; private set; }

[JsonProperty(&quot;stringId&quot;)]
public string stringId { get; private set; }
// ... other properties

}

but if you change the class names or just change &quot;type&quot; property of json string according to a class name, the code would be much more simple

List<Test> tests = JArray.Parse(json)
.Select(ja => (Test) ja.ToObject(Type
.GetType((string)ja["type"])))
.ToList();

}

public abstract class Test
{
[JsonProperty("type")]
public string type { get; private set; }

// or maybe better
//public string type { get {return this.GetType().Name; } }
[JsonProperty(&quot;stringId&quot;)]
public string stringId { get; private set; }
public string[] array { get; init;}
public byte ebyte { get; init;}
public short eshort { get; init; }
public bool ebool { get; init;}
public byte[] tuple { get; init; }

}

public class plus : Test
{
public string valuePlus { get; init;}
}

public class plusplus : Test
{
public string valuePlusPlus { get; init;}
}

public class plusplusplus : Test
{
public string valuePlusPlusPlus { get; init;}
}

huangapple
  • 本文由 发表于 2023年5月11日 08:53:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76223460.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定