英文:
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've been trying for quite a while and can't figure it out, and can't really find any videos or anything on how to do it.
Here'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<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("the json is invalid or something");
} 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'd prefer if it dynamically? added new fields I put into them.
</details>
# 答案1
**得分**: 2
以下是您提供的代码的中文翻译部分:
无法反序列化为C#类,如果类不允许设置属性,则无法反序列化。您至少需要使用`init`。此外,元组也无法反序列化。修复后,此代码将正常工作。
List
? ja.ToObject
: (string)ja["type"] == "plusplus"
? ja.ToObject
: ja.ToObject
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
.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'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("stringId")]
public string stringId { get; private set; }
// ... other properties
}
but if you change the class names or just change "type" 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("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;}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论