【发布时间】:2017-11-28 15:35:53
【问题描述】:
我正在使用 Json.NET 进行序列化,但结果字符串太长了,因为它包含大量关于我没有用的程序集的多余信息。
例如,这是我得到的其中一种类型:
"Assets.Logic.CompGroundType, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null": {
"$type": "Assets.Logic.CompGroundType, Assembly-CSharp",
"GroundType": 1,
"EntityID": 1,
"<GroundType>k__BackingField": 1
}
“GroundType”是一个枚举,“EntityID”是一个整数。
这是我想要的结果:
"Assets.Logic.CompGroundType" : {
"$type": "Assets.Logic.CompGroundType",
"GroundType": 1,
"EntityID": 1,
"<GroundType>k__BackingField": 1
}
如果可能的话,我还想删除“$type”字段,同时仍然正确反序列化继承的类型(我不确定为什么有必要,因为该信息是从上面的一行复制的,但如果我删除它通过设置 TypeNameHandling.None,我得到子类型的反序列化错误)。我也不确定最后一个字段 (k__BackingField) 的用途。
如果可能的话,我想进一步减少它,以:
"Assets.Logic.CompGroundType" : {
"GroundType": 1,
"EntityID": 1,
}
我知道可以为 Json.Net 中的每种类型手动自定义序列化方案,但我有数百种类型,所以我想通过一些全局设置自动完成。
我尝试更改“FormatterAssemblyStyle”,但那里没有“None”选项,只有“Simple”或“Full”,而且我已经在使用“Simple”了。
提前感谢您的帮助。
编辑:
请务必注意,类型是字典中的键。这就是该类型出现两次的原因(在第一个示例的第一行和第二行中)。
实现自定义 SerializationBinder 后,我能够减少“$type”字段的长度,但不能减少 Dictionary 键字段的长度。现在我得到以下信息:
"componentDict": {
"Assets.Logic.CompGroundType, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null": {
"$type": "Assets.Logic.CompGroundType",
"GroundType": 1,
"EntityID": 1,
"<GroundType>k__BackingField": 1
}
}
编辑 2:
我要序列化的代码是entity component system。我将尝试提供带有代码示例的详细示例。
所有组件(包括上面的CompGroundType)都继承自以下抽象类:
abstract class Component
{
public int EntityID { get; private set; }
protected Component(int EntityID)
{
this.EntityID = EntityID;
}
}
我遇到的问题是Entity类的序列化componentDict:
class Entity
{
readonly public int id;
private Dictionary<Type, Component> componentDict = new Dictionary<Type, Component>();
[JsonConstructor]
private Entity(Dictionary<Type, Component> componentDict, int id)
{
this.id = id;
this.componentDict = componentDict;
}
}
componentDict 包含附加到实体的所有组件。在每个条目<Type, Component>中,值的类型等于键。
我正在递归地进行序列化,使用以下JsonSerializerSettings:
JsonSerializerSettings serializerSettings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new MyContractResolver(),
TypeNameHandling = TypeNameHandling.Auto,
SerializationBinder = new TypesWithNoAssmeblyInfoBinder(),
Formatting = Formatting.Indented
}
其中MyContractResolver 与this answer 的一种形式相同。
TypesWithNoAssmeblyInfoBinder 从编辑 1 中进行更改:
private class TypesWithNoAssmeblyInfoBinder : ISerializationBinder
{
public Type BindToType(string assemblyName, string typeName)
{
return Type.GetType(typeName);
}
public void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.FullName;
}
}
序列化本身是这样完成的:
var jsonSerializer = JsonSerializer.Create(serializerSettings);
using (FileStream zippedFile = new FileStream(Application.persistentDataPath + fileName, FileMode.Create))
{
using (GZipStream archive = new GZipStream(zippedFile, CompressionLevel.Fastest))
{
using (StreamWriter sw = new StreamWriter(archive))
{
jsonSerializer.Serialize(sw, savedData);
}
}
}
编辑 4:
CompGroundType 类(已完成组件的示例):
class CompGroundType : Component
{
public enum Type {Grass, Rock};
public Type GroundType { get; private set; }
[JsonConstructor]
private CompGroundType(Type groundType, int entityID) : base(entityID)
{
this.GroundType = groundType;
}
}
【问题讨论】:
-
一种选择是使用来自Json serialization for Object Data Type 的
DefaultAssemblyBinder。另一个是来自JsonConverter how to deserialize to generic object 的SimpleAssemblyMappingSerializationBinder。 -
我已经按照您的示例建议的那样实现了自定义活页夹,但它似乎只是部分解决方案。请查看我的编辑。
-
我认为我们可能需要查看minimal reproducible example 以提供进一步帮助。您能否分享使用完整 Json.NET 库重现您正在使用的输出的 c# 类?
componentDict是Dictionary<Type, T>,其中T保证与字典键的类型相同? -
是的。字典是
Dictionary<Type, Component>,其中Component是一个被许多子类型继承的父类型。保证单个条目的Type和Component属于同一类型。上例中的Assets.Logic.CompGroundType就是这样一种继承类型。这是Entity Component System 实现的一部分。我很抱歉之前没有更清楚。如果您需要进一步的解释或代码示例,我会尝试提供。谢谢。 -
minimal reproducible example 仍然会有所帮助。例如。
k__BackingField属性的存在表明您设置了DefaultContractResolver.IgnoreSerializableAttribute = false,或者可能在某处设置了MemberSerialization = MemberSerialization.Fields。我们需要知道您启用了哪个,如果有的话。
标签: c# .net json serialization json.net