c# - Newtonsoft 调用 Getter 反序列化属性
问题描述
我创建了一个简单的类模型 ( AnchorMetaData
),如下所示,其中有两个项目。一个是列表字段Vector3
(SerializableVector3
我希望将此属性与 Newtonsoft 一起使用来保存/加载模型。
该类保存得很好但是,当我尝试从 JSON 反序列化模型时,它调用AttachedTaskLocations
属性的 getter 而不是 setter。这使得要初始化的字段为空。
我只是通过使用日志消息并设置一些断点才注意到这一点。反序列化时它从不调用设置器。这很奇怪,因为它应该起作用。
另一个奇怪的行为是它确实在 x、y、z 的设置器上暂停,并SerializableVector3
使用文件中的正确值。这太奇怪了。
我正在使用 Unity 2019.1.14,但没有它也应该可以工作,只需将矢量列表更改为您拥有的东西。
当我加载显示的 JSON 文件时,该文件是通过序列化创建的,AnchorMetaData
它在attachedTaskLocations
. 为什么会这样?为什么 setter 没有被调用?
我为保存/加载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
}
]
}
解决方案
AttachedTaskLocations
反序列化后 your 为空的原因有两个:
- 默认情况下,Json.Net 将在反序列化期间重用现有对象值,而不是创建新对象值。因此,对于
AttachedTaskLocations
列表等属性,它将首先调用 getter,然后找到现有值,然后继续从 JSON 填充它。 - 您的吸气剂
AttachedTaskLocations
不会每次都返回相同的实例;它总是从attachedTaskLocations
支持字段创建一个新实例。
所以似乎正在发生的是:
- 序列化程序调用
AttachedTaskLocations
getter,它返回一个新的空列表。 - 序列化程序从 JSON 填充该列表。
- 填充的列表被丢弃(序列化程序假定
AnchorMetaData
实例已经具有对列表的引用,因此它永远不会调用设置器)。 - 当您稍后访问
AttachedTaskLocations
getter 时,它会再次返回一个新的空列表。
您可以通过将ObjectCreationHandling
设置设置为来更改序列化程序的行为Replace
。仅此更改似乎就解决了我的测试中的问题。
Vector3
但是,我认为当有更好的解决方案时,您在这里跳过一堆箍来正确序列化/反序列化:使用自定义JsonConverter
. 这是转换器所需的代码。没那么多:
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
例如:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new Vector3Converter());
var metaData = JsonConvert.DeserializeObject<AnchorMetaData>(json, settings);
往返演示:https ://dotnetfiddle.net/jmYIq9
如果您无权访问序列化程序(很难从您的问题中判断您是在自己的代码中进行序列化/反序列化,还是某些第三方组件正在处理),那么使用转换器的另一种方法是通过属性。对于像这样的列表属性,AttachedTaskLocations
您可以ItemConverterType
在属性中指定权限,[JsonProperty]
如下所示:
[JsonProperty("AttachedTaskLocations", ItemConverterType = typeof(Vector3Converter))]
public List<Vector3> AttachedTaskLocations { get; set; } = new List<Vector3>();
如果您有一个实例属性,那么您将使用一个[JsonConverter]
属性,如下所示:
[JsonConverter(typeof(Vector3Converter))]
public Vector3 SingleVector { get; set; }
推荐阅读
- java - 如何在java中获得白天的均值和标准差
- powerbi - Power BI 直接查询优化和要使用的适当数据量
- python - Python 联合 Blob 检测
- c - C访问结构中的第一个字母
- java - 使用 Servlet 设置图像的路径
- python - 如何在 DataFrame 的某些行中使用定义的函数?
- reactjs - 嵌套导航器
- android - 为什么Android Studio可以在真实设备上运行应用程序并安装失败?
- flutter - 键入'_InternalLinkedHashMap
' 不是 'RxList 类型的子类型 ' - mysql - 该网站过去运行良好,但突然间我遇到了 DB 错误 1064