C# 我可以用Json.net在一次操作中将嵌套属性序列化到类中吗?

C# 我可以用Json.net在一次操作中将嵌套属性序列化到类中吗?,c#,multithreading,json.net,C#,Multithreading,Json.net,假设我有一个这样的模型: public class MyModel { public string Name { get; set; } public string[] Size { get; set; } public string Weight { get; set; } } 像这样: { "name" : "widget", "details" : { "size" : [ "XL","M","S",

假设我有一个这样的模型:

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

}
像这样:

{
    "name" : "widget",
    "details" : {
        "size" : [
            "XL","M","S",
        ]
        "weight" : "heavy"
    }
}
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<MyModel>("your json string...");
var myModel = new MyModel { Name = "widget", Size = new[] { "XL", "M", "S" }, Weight = "heavy" };
string serializedObject = JsonConvert.SerializeObject(myModel);
[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<MyModel>(json);
        Debug.WriteLine(JsonConvert.SerializeObject(mod, Formatting.Indented));
    }
}
我一直在试图找到一种方法来序列化我的对象,而不需要为“名称”和“细节”创建一个模型,因为这不能很好地映射到我的数据库,所以需要进行一些处理来填充类

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

但在我的真实代码中,我运行多个线程,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 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<string>(reader);
            }
            else if (string.Equals("size", propertyName, StringComparison.OrdinalIgnoreCase))
            {
                if (!inDetails)
                {
                    throw new JsonSerializationException();
                }

                reader.Read();
                result.Size = serializer.Deserialize<string[]>(reader);
            }
            else if (string.Equals("weight", propertyName, StringComparison.OrdinalIgnoreCase))
            {
                if (!inDetails)
                {
                    throw new JsonSerializationException();
                }

                reader.Read();
                result.Weight = serializer.Deserialize<string>(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; }
}
公共类MyModelJsonConverter:JsonConverter
{
公共覆盖布尔可读取
{
获取{return true;}
}
公共覆盖布尔CanConvert(类型objectType)
{
返回objectType==typeof(MyModel);
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
if(objectType!=typeof(MyModel))
{
抛出新ArgumentException(“objectType”);
}
开关(reader.TokenType)
{
案例JsonToken.Null:
{
返回null;
}
案例JsonToken.StartObject:
{
reader.Read();
打破
}
违约:
{
抛出新的JsonSerializationException();
}
}
var结果=新的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(读取器);
}
else if(string.Equals(“size”、propertyName、StringComparison.OrdinalIgnoreCase))
{
如果(!inDetails)
{
抛出新的JsonSerializationException();
}
reader.Read();
result.Size=serializer.Deserialize(读取器);
}
else if(string.Equals(“weight”、propertyName、StringComparison.OrdinalIgnoreCase))
{
如果(!inDetails)
{
抛出新的JsonSerializationException();
}
reader.Read();
result.Weight=序列化程序.反序列化(读取器);
}
else if(string.Equals(“详细信息”、propertyName、StringComparison.OrdinalIgnoreCase))
{
reader.Read();
if(reader.TokenType!=JsonToken.StartObject)
{
抛出新的JsonSerializationException();
}
inDetails=真;
}
其他的
{
reader.Skip();
}
reader.Read();
}
如果(详细信息)
{
if(reader.TokenType!=JsonToken.EndObject)
{
抛出新的JsonSerializationException();
}
reader.Read();
}
返回结果;
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
如果(值==null)
{
WriteNull();
返回;
}
var模型=作为MyModel的值;
如果(model==null)抛出新的JsonSerializationException();
writer.WriteStartObject();
writer.WritePropertyName(“名称”);
writer.WriteValue(model.Name);
writer.WritePropertyName(“详细信息”);
writer.WriteStartObject();
writer.WritePropertyName(“大小”);
serializer.Serialize(writer、model.Size);
writer.WritePropertyName(“重量”);
writer.WriteValue(型号、重量);
writer.WriteEndObject();
writer.WriteEndObject();
}
}
[JsonConverter(类型(MyModelJsonConverter))]
公共类MyModel
{
公共字符串名称{get;set;}
公共字符串[]大小{get;set;}
公共字符串权重{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<MyModel>(output);
/*
{
    Name = "widget",
    Size = [ "XL", "M", "S" ],
    Weight = "heavy"
}
*/
var模型=新的MyModel
{
Name=“widget”,
大小=新[]{“XL”、“M”、“S”},
重量=“重”
};
字符串输出=JsonConvert.SerializeObject(模型);
//{“name”:“widget”,“details”:{“size”:[“XL”,“M”,“S”],“weight”:“heavy”}
var model2=JsonConvert.DeserializeObject(输出);
/*
{
Name=“widget”,
尺寸=[“XL”、“M”、“S”],
重量=“重”
}
*/

您只需在模型中为
详细信息使用一个额外字段,并使用
JsonIgnore
属性忽略
大小
重量
字段的序列化即可。因此,您的模型将如下所示:

{
    "name" : "widget",
    "details" : {
        "size" : [
            "XL","M","S",
        ]
        "weight" : "heavy"
    }
}
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<MyModel>("your json string...");
var myModel = new MyModel { Name = "widget", Size = new[] { "XL", "M", "S" }, Weight = "heavy" };
string serializedObject = JsonConvert.SerializeObject(myModel);
[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<MyModel>(json);
        Debug.WriteLine(JsonConvert.SerializeObject(mod, Formatting.Indented));
    }
}
然后,您可以像这样简单地序列化/反序列化您的模型:

{
    "name" : "widget",
    "details" : {
        "size" : [
            "XL","M","S",
        ]
        "weight" : "heavy"
    }
}
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<MyModel>("your json string...");
var myModel = new MyModel { Name = "widget", Size = new[] { "XL", "M", "S" }, Weight = "heavy" };
string serializedObject = JsonConvert.SerializeObject(myModel);
[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<MyModel>(json);
        Debug.WriteLine(JsonConvert.SerializeObject(mod, Formatting.Indented));
    }
}
var deserializedModel=JsonConvert.DeserializeObject(“您的json字符串…”);
var myModel=newmymodel{Name=“widget”,Size=new[]{“XL”、“M”、“S”},Weight=“heavy”};
string serializedObject=JsonConvert.SerializeObject(myModel);

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

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<bool>(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<T> : IDisposable
{
    Action<T> setValue;
    T oldValue;

    public PushValue(T value, Func<T> getValue, Action<T> 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
}
公共类MyModelConverter:JsonConverter
{
[线程静态]
静态布尔不能写;
//以线程安全的方式禁用转换器。
bool CannotWrite{get{return CannotWrite;}set{CannotWrite=value;}
公共重写bool CanWrite{get{return!cannotwrite