我可以使用 Json.net 在一次操作中将嵌套属性序列化到我的类吗? [英] Can I serialize nested properties to my class in one operation with Json.net?

查看:24
本文介绍了我可以使用 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"
    }
}

我一直在尝试找出一种方法来序列化我的对象,而无需为名称"创建一个模型,为详细信息"创建一个模型,因为这不能很好地映射到我的数据库,因此需要一些杂耍才能获得类已填充.

I have been trying to work out a way to serialize my object without making one model for the "name" and one model for the "details" as this doesn't map nicely to my database so involves a little juggling to get class populated.

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

I can make multiple passes at JsonConvert.PopulateObject() like:

var mod = new MyModel();

JsonConvert.PopulateObject(json.ToString(), mod);
JsonConvert.PopulateObject(json["details"].ToString(), mod);

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

But in my real code I am running multiple threads and PopulateObject is not thread safe, it jams the app. The comments for PopulateJsonAsync() say not to use it but instead to use Task.Run() on PopulateObject().

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

This does not work and still locks the app when I call it like:

await Task.Run(() => JsonConvert.PopulateObject(response.ToString(), productDetail));

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

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

A few get through but eventually the app gets completely thread locked. If I remove PopulateObject the threads all terminate fine so I am pretty sure this function is not thread safe.

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

Is there a neat threadsafe approach to populating my object in a single step?

推荐答案

您可以使用以下转换器:

You can do it with the following converter:

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
}

然后像这样使用它:

[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));
    }
}

ReadJson() 方法很简单:反序列化为 JObject,重构适当的属性,然后填充 MyModel 类.WriteJson 有点别扭;转换器需要以线程安全的方式暂时禁用自身以生成默认"JObject,然后可以对其进行重构.

The ReadJson() method is straightforward: deserialize to a JObject, restructure the appropriate properties, then populate the MyModel class. WriteJson is a little more awkward; the converter needs to temporarily disable itself in a thread-safe manner to generate a "default" JObject that can be then restructured.

这篇关于我可以使用 Json.net 在一次操作中将嵌套属性序列化到我的类吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆