Могу ли я сериализовать вложенные свойства в мой 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" } */ 
  • Является ли доступ к переменной в C # атомной операцией?
  • В чем разница между ожиданием Task и Task .Result?
  • Java: как использовать Thread.join
  • Как обновить текстовое поле в графическом интерфейсе из другого streamа
  • Много процессов обрабатывают и записывают один файл
  • Использование ThreadPool.QueueUserWorkItem в ASP.NET в сценарии с высоким трафиком
  • Java: Как остановить stream?
  • Каков правильный способ реализации QThread ... (пример, пожалуйста ...)
  • c ++ рабочие очереди с блокировкой
  • C ++ Threads, std :: system_error - операция не разрешена?
  • вызывающий thread.start () внутри своего собственного конструктора
  • Давайте будем гением компьютера.