我创建了一个简单的类模型(AnchorMetaData
),如下所示,其中有两个项目。一个是列表字段(Vector3
),由于它的制作方式而无法序列化,因此我为类(SerializableVector3
)创建了可以序列化和反序列化的属性。我希望与Newtonsoft一起使用此属性来保存/加载模型。
该类保存得很好,但是,当我尝试从JSON反序列化模型时,它会调用AttachedTaskLocations
属性的getter而不是setter。这使得要初始化的字段为空。
我仅通过使用日志消息并设置一些断点来注意到这一点。反序列化时,它从不调用setter。这很奇怪,因为它应该起作用。
另一个奇怪的行为是,它确实使用文件中的正确值在SerializableVector3
的x,y,z的设置器上暂停。这太奇怪了。
我正在使用Unity 2019.1.14,但这也可以在没有它的情况下工作,只需将向量列表更改为您拥有的东西即可。
当我加载显示的JSON文件时,该文件是通过序列化AnchorMetaData
创建的,它在attachedTaskLocations
中有零个项目。为什么会这样呢?为什么二传手没被叫到?
我为保存/加载Vector3
创建的类称为SerializableVector3
。
我要保存/加载的班级:
[Serializable]
public class AnchorMetaData
{
// Cannot serialize this.
[JsonIgnore]
public List<Vector3> attachedTaskLocations = new List<Vector3>();
/// <summary>
/// This property servers as an interface for JSON de-/serialization.
/// It uses a class that can be serialized by Newtonsoft.
/// Should not be used in code except for serialization purposes.
/// </summary>
[JsonProperty("AttachedTaskLocations")]
public List<SerializableVector3> AttachedTaskLocations
{
get
{
Debug.Log("Writing serialized vector.");
return attachedTaskLocations
.Select(vector3 => new SerializableVector3(vector3))
.ToList();
}
set
{
Debug.Log("Loading serialized vector.");
attachedTaskLocations = value
.Select(sVector3 => new Vector3(sVector3.x, sVector3.y, sVector3.z))
.ToList();
}
}
}
序列化JSON:
{
"AttachedTaskLocations": [
{
"x": 1.0,
"y": 1.0,
"z": 1.0
},
{
"x": 1E+12,
"y": 2.0,
"z": 3.0
},
{
"x": 0.0,
"y": 0.0,
"z": 0.0
}
]
}
答案 0 :(得分:3)
反序列化后AttachedTaskLocations
为空的原因有两个:
AttachedTaskLocations
列表之类的属性,它将首先调用getter,然后查找现有值,然后它将继续从JSON填充它。AttachedTaskLocations
的getter每次不会返回相同的实例;它总是从attachedTaskLocations
后备字段创建一个新实例。所以看来是这样的:
AttachedTaskLocations
实例已经具有对该列表的引用,因此它永远不会调用setter)。AnchorMetaData
获取器时,它将再次返回一个新的空列表。您可以通过将ObjectCreationHandling
设置设置为AttachedTaskLocations
来更改串行器的行为。仅此一项更改似乎就可以解决问题in my testing。
但是,我认为您在这里遇到了很多麻烦,以便在有更好的解决方案时使用Replace
正确地序列化/反序列化:使用自定义JsonConverter
。这是转换器所需的代码。不算什么:
Vector3
有了此转换器,您就可以完全摆脱public class Vector3Converter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Vector3);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject obj = JObject.Load(reader);
return new Vector3((float)obj["x"], (float)obj["y"], (float)obj["z"]);
}
if (reader.TokenType == JsonToken.Null)
{
return null;
}
throw new JsonException("Unexpected token type: " + reader.TokenType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value != null)
{
Vector3 vector = (Vector3)value;
JObject obj = new JObject(
new JProperty("x", vector.x),
new JProperty("y", vector.y),
new JProperty("z", vector.z)
);
obj.WriteTo(writer);
}
else
{
JValue.CreateNull().WriteTo(writer);
}
}
}
类,并且可以将SerializableVector3
类简化如下:
AnchorMetaData
要使用转换器,您可以:
public class AnchorMetaData
{
[JsonProperty("AttachedTaskLocations")]
public List<Vector3> AttachedTaskLocations { get; set; } = new List<Vector3>();
}
/ JsonConvert.SerializeObject()
方法; DeserializeObject()
上的Converters
集合中,并将设置传递到JsonSerializerSettings
/ JsonConvert.SerializeObject()
,或者DeserializeObject()
实例上将其添加到Converters
集合中。例如:
JsonSerializer
往返演示:https://dotnetfiddle.net/jmYIq9
如果您无权访问序列化程序(很难从您的问题中得知您是在使用自己的代码进行序列化/反序列化,还是某些第三方组件正在处理该序列化程序),则可以使用另一种使用方式转换器通过属性。对于var settings = new JsonSerializerSettings();
settings.Converters.Add(new Vector3Converter());
var metaData = JsonConvert.DeserializeObject<AnchorMetaData>(json, settings);
之类的列表属性,您可以像这样在AttachedTaskLocations
属性中指定ItemConverterType
:
[JsonProperty]
如果您具有单个实例属性,则应使用 [JsonProperty("AttachedTaskLocations", ItemConverterType = typeof(Vector3Converter))]
public List<Vector3> AttachedTaskLocations { get; set; } = new List<Vector3>();
属性,如下所示:
[JsonConverter]