Могу ли я сериализовать вложенные свойства в мой class за одну операцию с помощью Json.net?

Допустим, у меня есть модель вроде:

public class MyModel { public string Name { get; set; } public string[] Size { get; set; } public string Weight { get; set; } } 

И Json вот так:

 { "name" : "widget", "details" : { "size" : [ "XL","M","S", ] "weight" : "heavy" } } 

Я пытаюсь разработать способ сериализации моего объекта, не создавая одну модель для «имени» и одной модели для «деталей», так как это не хорошо отображает мою базу данных, поэтому требует небольшого манипулирования, чтобы получить class.

Я могу сделать несколько проходов в JsonConvert.PopulateObject (), например:

 var mod = new MyModel(); JsonConvert.PopulateObject(json.ToString(), mod); JsonConvert.PopulateObject(json["details"].ToString(), mod); 

Но в моем реальном коде я запускаю несколько streamов, а PopulateObject не является streamобезопасным, это замявает приложение. Комментарии PopulationJsonAsync () говорят, что не использовать его, а вместо этого использовать Task.Run () для PopulateObject ().

Это не работает и по-прежнему блокирует приложение, когда я его называю:

 await Task.Run(() => JsonConvert.PopulateObject(response.ToString(), productDetail)); if (response["results"].HasValues) { await Task.Run(() => JsonConvert.PopulateObject(response["results"][0].ToString(), productDetail)); } 

Некоторые из них проходят, но в конечном итоге приложение полностью блокируется streamом. Если я удалю PopulateObject, все streamи прекратятся, поэтому я уверен, что эта функция не является streamобезопасной.

Есть ли аккуратный streamобезопасный подход к заполнению моего объекта за один шаг?

Вы можете сделать это со следующим конвертером:

 public class MyModelConverter : JsonConverter { [ThreadStatic] static bool cannotWrite; // Disables the converter in a thread-safe manner. bool CannotWrite { get { return cannotWrite; } set { cannotWrite = value; } } public override bool CanWrite { get { return !CannotWrite; } } public override bool CanConvert(Type objectType) { return typeof(MyModel).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var obj = JObject.Load(reader); obj.SelectToken("details.size").MoveTo(obj); obj.SelectToken("details.weight").MoveTo(obj); using (reader = obj.CreateReader()) { // Using "populate" avoids infinite recursion. existingValue = (existingValue ?? new MyModel()); serializer.Populate(reader, existingValue); } return existingValue; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // Disabling writing prevents infinite recursion. using (new PushValue(true, () => CannotWrite, val => CannotWrite = val)) { var obj = JObject.FromObject(value, serializer); var details = new JObject(); obj.Add("details", details); obj["size"].MoveTo(details); obj["weight"].MoveTo(details); obj.WriteTo(writer); } } } public static class JsonExtensions { public static void MoveTo(this JToken token, JObject newParent) { if (newParent == null) throw new ArgumentNullException(); if (token != null) { if (token is JProperty) { token.Remove(); newParent.Add(token); } else if (token.Parent is JProperty) { token.Parent.Remove(); newParent.Add(token.Parent); } else { throw new InvalidOperationException(); } } } } public struct PushValue : IDisposable { Action setValue; T oldValue; public PushValue(T value, Func getValue, Action setValue) { if (getValue == null || setValue == null) throw new ArgumentNullException(); this.setValue = setValue; this.oldValue = getValue(); setValue(value); } #region IDisposable Members // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class. public void Dispose() { if (setValue != null) setValue(oldValue); } #endregion } 

И затем используйте его так:

 [JsonConverter(typeof(MyModelConverter))] public class MyModel { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("size")] public string[] Size { get; set; } [JsonProperty("weight")] public string Weight { get; set; } } public class TestClass { public static void Test() { string json = @"{ ""name"" : ""widget"", ""details"" : { ""size"" : [ ""XL"",""M"",""S"", ], ""weight"" : ""heavy"" } }"; var mod = JsonConvert.DeserializeObject(json); Debug.WriteLine(JsonConvert.SerializeObject(mod, Formatting.Indented)); } } 

Метод ReadJson() прост: десериализуйте в JObject , перестройте соответствующие свойства, затем заполните class MyModel . WriteJson немного более WriteJson ; конвертер должен временно отключить себя поточно-безопасным способом для создания JObject умолчанию, который может быть затем реструктурирован.

Вы можете просто использовать свою модель с дополнительным полем для details и использовать атрибут JsonIgnore чтобы игнорировать сериализацию полей Size и Weight . Таким образом, ваша модель будет выглядеть так:

 public class MyModel { [JsonProperty("name")] public string Name { get; set; } public Details details { get; set; } [JsonIgnore] public string[] Size { get { return details != null ? details.size : null; } set { if (details == null) { details = new Details(); } details.size = value; } } [JsonIgnore] public string Weight { get { return details != null ? details.weight : null; } set { if (details == null) { details = new Details(); } details.weight = value; } } } 

то вы можете просто сериализовать / десериализовать модель следующим образом:

 var deserializedModel = JsonConvert.DeserializeObject("your json string..."); var myModel = new MyModel { Name = "widget", Size = new[] { "XL", "M", "S" }, Weight = "heavy" }; string serializedObject = JsonConvert.SerializeObject(myModel); 

Это должно работать:

 public class MyModelJsonConverter : JsonConverter { public override bool CanRead { get { return true; } } public override bool CanConvert(Type objectType) { return objectType == typeof(MyModel); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (objectType != typeof(MyModel)) { throw new ArgumentException("objectType"); } switch (reader.TokenType) { case JsonToken.Null: { return null; } case JsonToken.StartObject: { reader.Read(); break; } default: { throw new JsonSerializationException(); } } var result = new MyModel(); bool inDetails = false; while (reader.TokenType == JsonToken.PropertyName) { string propertyName = reader.Value.ToString(); if (string.Equals("name", propertyName, StringComparison.OrdinalIgnoreCase)) { reader.Read(); result.Name = serializer.Deserialize(reader); } else if (string.Equals("size", propertyName, StringComparison.OrdinalIgnoreCase)) { if (!inDetails) { throw new JsonSerializationException(); } reader.Read(); result.Size = serializer.Deserialize(reader); } else if (string.Equals("weight", propertyName, StringComparison.OrdinalIgnoreCase)) { if (!inDetails) { throw new JsonSerializationException(); } reader.Read(); result.Weight = serializer.Deserialize(reader); } else if (string.Equals("details", propertyName, StringComparison.OrdinalIgnoreCase)) { reader.Read(); if (reader.TokenType != JsonToken.StartObject) { throw new JsonSerializationException(); } inDetails = true; } else { reader.Skip(); } reader.Read(); } if (inDetails) { if (reader.TokenType != JsonToken.EndObject) { throw new JsonSerializationException(); } reader.Read(); } return result; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { writer.WriteNull(); return; } var model = value as MyModel; if (model == null) throw new JsonSerializationException(); writer.WriteStartObject(); writer.WritePropertyName("name"); writer.WriteValue(model.Name); writer.WritePropertyName("details"); writer.WriteStartObject(); writer.WritePropertyName("size"); serializer.Serialize(writer, model.Size); writer.WritePropertyName("weight"); writer.WriteValue(model.Weight); writer.WriteEndObject(); writer.WriteEndObject(); } } [JsonConverter(typeof(MyModelJsonConverter))] public class MyModel { public string Name { get; set; } public string[] Size { get; set; } public string Weight { get; set; } } 

С атрибутом classа, используя его так же просто:

 var model = new MyModel { Name = "widget", Size = new[] { "XL", "M", "S" }, Weight = "heavy" }; string output = JsonConvert.SerializeObject(model); // {"name":"widget","details":{"size":["XL","M","S"],"weight":"heavy"}} var model2 = JsonConvert.DeserializeObject(output); /* { Name = "widget", Size = [ "XL", "M", "S" ], Weight = "heavy" } */ 
  • Постоянно обновлять пользовательский интерфейс в рабочем streamе Java FX
  • Является ли функция EndInvoke () опциональной, необязательной или необязательной?
  • Как вызвать метод с отдельным streamом в Java?
  • Как правильно реализовать BackgroundWorker с обновлениями ProgressBar?
  • Показать прогрессDialog Android
  • Многопоточная сортировка или слияние
  • ObservableCollection и резьба
  • Как проверить, работает ли std :: thread?
  • Убийство streamа .NET
  • Связаны ли в LinkedBlockingQueue методы удаления и удаления?
  • Попытка запуска нескольких HTTP-запросов параллельно, но ограничена Windows (реестром)
  • Interesting Posts

    Как фильтровать определенные приложения для намерения ACTION_SEND (и устанавливать другой текст для каждого приложения)

    Как правильно выполнять фоновый stream при использовании Spring Data и Hibernate?

    ConfigurationManager не сохраняет настройки

    Как я могу захватить пакеты, идущие от моего маршрутизатора к определенному серверу?

    Как я могу использовать символ Unicode для сравнения символов?

    Бесконечный ViewPager

    Можно ли настроить TrueCrypt для демонтажа томов во время спячки?

    Проблемы с методом Paint в Java, смехотворная скорость обновления

    urlencoded

    понимание базовой рекурсии

    ViewController.Type не имеет члена с именем

    Можете ли вы писать виртуальные функции / методы на Java?

    Что такое правильное значение «-moz-appearance», чтобы скрыть стрелку вниз элемента

    Используют ли веб-браузер разные исходящие порты для разных вкладок?

    Как реализовать сопоставимый интерфейс Java?

    Давайте будем гением компьютера.