如何反序列化 json class 包含相同 class 的嵌套(Unity C#)?
How to deserialize json class containing a nesting of the same class (Unity C#)?
我有一个 class Person.cs
需要存储好友列表(Person.cs
列表)。但是,将整个 Person 信息存储在该数组中,只是为了拥有一个完整的对象,就太过分了。
Json 文件
[
{
"Username": "mary",
"DisplayName": "Mary Sanchez",
"Age": 20,
"Friends": [
{"Username": "jonathan"},
{"Username": "katy"}
]
},
{
"Username": "jonathan",
"Age": 25,
"DisplayName": "Jonathan White",
"Friends": [
{"Username": "mary"},
{"Username": "katy"}
]
},
{
"Username": "katy",
"DisplayName": "Katy Rivers",
"Age": 28,
"Friends": [
{"Username": "mary"},
{"Username": "jonathan"}
]
}
]
C#class
public class Person
{
public string Username { get; set; }
public string DisplayName { get; set; }
public int Age { get; set; }
public List<Person> Friends { get; set; }
}
如您所见,属性 Friends
是 Person
的数组。重新定义 json 文件的朋友数组中的所有人对我来说太过分了,尤其是当它是一个大文件时。我无法实例化 'mary',因为她的朋友 'jonathan' 和 'katy' 稍后会出现在 json 文件中。如您所见,这变成了一个嵌套依赖问题。 还假设这个文件有很多循环引用,因为每个人都是彼此的朋友。
问题当然是我最终会在 Friends
的数组中得到不完整的 Person 对象。如果我想检索 mary 的朋友年龄,它将导致空值:
StreamReader reader = new StreamReader("Assets/Resources/Json/persons.json");
Persons = JsonConvert.DeserializeObject<List<Person>>(reader.ReadToEnd());
Person mary = Persons.Find(i => i.Username == "mary");
foreach(Person friend in mary.friends){
int age = friend.Age; //this will be null
}
所以我尝试通过再次循环列表并手动检索 friend 变量来修复它:
foreach (Person friend in mary.Friends)
{
Person friend = PersonManager.Persons.Find(i => i.Username == profile.Username);
int age = friend.age;
}
这可行,但效率很低。特别是如果我有更多类似 Friends
属性 的属性。请注意,Persons
数组位于单独的 class 中,我必须将列表设为静态以便始终检索它(我相信这也是不好的做法)。有没有更好的方法来一次实现我想要实现的目标?
顺便说一句,您的 JSON 无效。您需要查看“Jonathan”,了解该记录如何具有两个 UserName
属性而没有 DisplayName
。我假设第二次出现应该命名为 DisplayName
.
快速简便的解决方案是 to use PreserveReferencesHandling
,但这也需要首先由生成 JSON 的代码使用。如果您无法控制正在生成的 JSON 和 ,您希望继续使用 Json.NET(又名 Newtonsoft.Json
),那么您需要定义一个单独的 DTO 类型(不要 使用 class 继承来避免在 DTO 类型和 business/domain 实体类型之间复制+粘贴属性,这不是继承for - 如果您担心单调乏味,那么使用 T4 从共享的属性列表中生成 partial
类型)。
所以添加这些class是:
public class PersonDto
{
public string UserName { get; set; }
public string DisplayName { get; set; }
public int Age { get; set; }
public List<PersonRef> Friends { get; set; }
}
public class PersonRef
{
public string UserName { get; set; }
}
然后把你的反序列化代码改成这样:
public List<Person> GetPeople( String jsonText )
{
List<PersonDto> peopleDto = JsonConvert.DeserializeObject< List<PersonDto> >( jsonText );
List<Person> people = peopleDto
.Select( p => ToPersonWithoutFriends( p ) )
.ToList();
Dictionary<String,PersonDto> peopleDtoByUserName = peopleDto.ToDictionary( pd => pd.UserName );
Dictionary<String,Person> peopleByUserName = people.ToDictionary( p => p.UserName );
foreach( Person p in people )
{
PersonDto pd = peopleDtoByUserName[ p.UserName ];
p.Friends.AddRange(
pd.Friends.Select( pr => peopleByUserName[ pr.UserName ] )
);
}
return people;
}
private static Person ToPersonWithoutFriends( PersonDto dto )
{
return new Person()
{
UserName = dto.UserName,
DisplayName = dto.DisplayName,
Age = dto.Age,
Friends = new List<Person>()
};
}
我有一个 class Person.cs
需要存储好友列表(Person.cs
列表)。但是,将整个 Person 信息存储在该数组中,只是为了拥有一个完整的对象,就太过分了。
Json 文件
[
{
"Username": "mary",
"DisplayName": "Mary Sanchez",
"Age": 20,
"Friends": [
{"Username": "jonathan"},
{"Username": "katy"}
]
},
{
"Username": "jonathan",
"Age": 25,
"DisplayName": "Jonathan White",
"Friends": [
{"Username": "mary"},
{"Username": "katy"}
]
},
{
"Username": "katy",
"DisplayName": "Katy Rivers",
"Age": 28,
"Friends": [
{"Username": "mary"},
{"Username": "jonathan"}
]
}
]
C#class
public class Person
{
public string Username { get; set; }
public string DisplayName { get; set; }
public int Age { get; set; }
public List<Person> Friends { get; set; }
}
如您所见,属性 Friends
是 Person
的数组。重新定义 json 文件的朋友数组中的所有人对我来说太过分了,尤其是当它是一个大文件时。我无法实例化 'mary',因为她的朋友 'jonathan' 和 'katy' 稍后会出现在 json 文件中。如您所见,这变成了一个嵌套依赖问题。 还假设这个文件有很多循环引用,因为每个人都是彼此的朋友。
问题当然是我最终会在 Friends
的数组中得到不完整的 Person 对象。如果我想检索 mary 的朋友年龄,它将导致空值:
StreamReader reader = new StreamReader("Assets/Resources/Json/persons.json");
Persons = JsonConvert.DeserializeObject<List<Person>>(reader.ReadToEnd());
Person mary = Persons.Find(i => i.Username == "mary");
foreach(Person friend in mary.friends){
int age = friend.Age; //this will be null
}
所以我尝试通过再次循环列表并手动检索 friend 变量来修复它:
foreach (Person friend in mary.Friends)
{
Person friend = PersonManager.Persons.Find(i => i.Username == profile.Username);
int age = friend.age;
}
这可行,但效率很低。特别是如果我有更多类似 Friends
属性 的属性。请注意,Persons
数组位于单独的 class 中,我必须将列表设为静态以便始终检索它(我相信这也是不好的做法)。有没有更好的方法来一次实现我想要实现的目标?
顺便说一句,您的 JSON 无效。您需要查看“Jonathan”,了解该记录如何具有两个 UserName
属性而没有 DisplayName
。我假设第二次出现应该命名为 DisplayName
.
快速简便的解决方案是 to use PreserveReferencesHandling
,但这也需要首先由生成 JSON 的代码使用。如果您无法控制正在生成的 JSON 和 ,您希望继续使用 Json.NET(又名 Newtonsoft.Json
),那么您需要定义一个单独的 DTO 类型(不要 使用 class 继承来避免在 DTO 类型和 business/domain 实体类型之间复制+粘贴属性,这不是继承for - 如果您担心单调乏味,那么使用 T4 从共享的属性列表中生成 partial
类型)。
所以添加这些class是:
public class PersonDto
{
public string UserName { get; set; }
public string DisplayName { get; set; }
public int Age { get; set; }
public List<PersonRef> Friends { get; set; }
}
public class PersonRef
{
public string UserName { get; set; }
}
然后把你的反序列化代码改成这样:
public List<Person> GetPeople( String jsonText )
{
List<PersonDto> peopleDto = JsonConvert.DeserializeObject< List<PersonDto> >( jsonText );
List<Person> people = peopleDto
.Select( p => ToPersonWithoutFriends( p ) )
.ToList();
Dictionary<String,PersonDto> peopleDtoByUserName = peopleDto.ToDictionary( pd => pd.UserName );
Dictionary<String,Person> peopleByUserName = people.ToDictionary( p => p.UserName );
foreach( Person p in people )
{
PersonDto pd = peopleDtoByUserName[ p.UserName ];
p.Friends.AddRange(
pd.Friends.Select( pr => peopleByUserName[ pr.UserName ] )
);
}
return people;
}
private static Person ToPersonWithoutFriends( PersonDto dto )
{
return new Person()
{
UserName = dto.UserName,
DisplayName = dto.DisplayName,
Age = dto.Age,
Friends = new List<Person>()
};
}