我可以使用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" } } 

我一直试图找到一种方法来序列化我的对象,而不是为“名称”创建一个模型,而为“详细信息”创建一个模型,因为这不能很好地映射到我的数据库,所以需要一点点玩杂耍来填充类。

我可以在JsonConvert.PopulateObject()上进行多次传递,如:

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

但在我的真实代码中,我运行多个线程,而PopulateObject不是线程安全的,它会阻塞应用程序。 PopulateJsonAsync()的注释表示不使用它,而是在PopulateObject()上使用Task.Run()。

这不起作用,当我调用它时仍然锁定应用程序:

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

一些人通过,但最终应用程序完全线程锁定。 如果我删除PopulateObject线程都终止正常,所以我很确定这个函数不是线程安全的。

是否有一个简洁的线程安全的方法来一步填充我的对象?

您可以使用以下转换器执行此操作:

 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 ,重构相应的属性,然后填充MyModel类。 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; } } 

使用类的属性,使用它就像:

 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" } */