Причина того, что ваша AttachedTaskLocations
пуста после десериализации, двояка:
- По умолчанию Json.Net будет повторно использовать существующие значения объектов во время десериализации, а не создавать новые. Таким образом, для таких свойств, как ваш список
AttachedTaskLocations
, он сначала вызовет метод получения, а затем, найдя существующее значение, продолжит заполнять его из JSON. - Получатель вашего
AttachedTaskLocations
не возвращает один и тот же экземпляр каждый раз;он всегда создает новый экземпляр из поля поддержки attachedTaskLocations
.
Похоже, происходит следующее:
- Сериализатор вызывает геттер
AttachedTaskLocations
,который возвращает новый пустой список. - Сериализатор заполняет этот список из JSON.
- Заполненный список отбрасывается (сериализатор предполагает, что экземпляр
AnchorMetaData
уже имеет ссылку насписок, так что он никогда не вызывает сеттер). - Когда вы позже получаете доступ к
AttachedTaskLocations
получателю, он снова возвращает новый пустой список.
Вы можете изменить поведение сериализатораустановив 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; }
Fiddle: https://dotnetfiddle.net/yxwqDL