Newtonsoft Json.NET JsonConverter属性保存引用反序列化的问题时,

问题描述 投票:1回答:1

在项目的模型我使用的是JsonConverter属性,以帮助这些模型的(反)序列。

该转换器目前看起来是这样的:

public class CustomJsonConverter : Newtonsoft.Json.JsonConverter
{
    bool _canWrite = true;
    public override bool CanWrite
    {
        get { return _canWrite; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        serializer.DefaultValueHandling = DefaultValueHandling.Ignore;
        serializer.NullValueHandling = NullValueHandling.Ignore;

        _canWrite = false;
        var jObject = JObject.FromObject(value, serializer);
        _canWrite = true;

        jObject.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        serializer.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        if (reader.TokenType == JsonToken.StartObject)
        {
            existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
            serializer.Populate(reader, existingValue);
            return existingValue;
        }
        else if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }
        else
        {
            throw new JsonSerializationException();
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(IModelBase).IsAssignableFrom(objectType);
    }
}

该机型有一个基类,看起来像这样:

[JsonConverter(typeof(CustomJsonConverter))]
public abstract class ModelBase : IModelBase
{
    public string ID { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime ModifiedAt { get; set; }
}

所述ModelBase类的派生类具有的类型也从ModelBase派生属性。例如:

public class CustomerModel : ModelBase
{
    public string Name { get; set; }
    public UserModel CreatedBy { get; set; }
    public UserModel ModifiedBy { get; set; }
}

public class UserModel : ModelBase
{
    public string Name { get; set; }
    public UserModel CreatedBy { get; set; }
    public UserModel ModifiedBy { get; set; }
}

我在ASP.NET Web API 2应用程序(服务器端),并在C#应用程序(客户端)使用这些模型。对于大多数API调用,则返回的车型阵列。当序列化的模型,东西正常工作。然而,反序列化时,仅每参考的第一次出现充满的信息。

例如:

[
    {
        "$id": "1",
        "Name": "Customer1",
        "CreatedBy": {
            "$id": "2",
            "ID": "1",
            "Name": "User1"
        },
        "ModifiedBy": {
            "$id": "3",
            "ID": "3",
            "Name": "User3"
        },
        "ID": "1",
        "CreatedAt": "2019-02-06T00:00:04",
        "ModifiedAt": "2019-02-06T00:20:12"
    },
    {
        "$id": "4",
        "Name": "Customer2",
        "CreatedBy": {
            "$ref": "2"
        },
        "ModifiedBy": {
            "$ref": "2"
        },
        "ID": "2",
        "CreatedAt": "2019-02-06T00:10:00",
        "ModifiedAt": "2019-02-06T00:10:00"
    }
]

当尝试反序列化由Web API返回此JSON对象中,CreatedByModifiedBy性能将是第一CustomerModel对象正确。对于第二CustomerModel对象,但是,这些属性将不设置任何性质的新UserModel实例。

反序列化JSON字符串,我使用下面的代码:

using (var sr = new StreamReader(streamFromWebAPICall))
{                
    using (var jtr = new JsonTextReader(sr))
    {
        var js = new JsonSerializer();
        return js.Deserialize(jtr, objectType);
    }
}

我能做些什么在所有反序列化对象设置正确的属性?

编辑:

这个问题似乎是在serializer.Populate(reader, existingValue),其中引用不记得。

c# serialization json.net deserialization json-deserialization
1个回答
2
投票

你的基本问题是,你提供的类型,你还想要启用custom JsonConverter一个PreserveReferencesHandling。但每当应用自定义转换器,必须手动打理一切,包括分析和"$ref""$id"性质发生。您的转换并没有这样做,因此您的反序列化的代码不正确地反序列化对象图。

accepted answerCustom object serialization vs PreserveReferencesHandling包括模板转换器,显示了这些特性可以处理。然而,由于你的转换器似乎只前(反)序列被触发一些串行设置,你可以简单地拨打电话,以递归(德)序列化对象,禁用该转换器的时间,就像这样:

public class CustomJsonConverter : Newtonsoft.Json.JsonConverter
{
    [ThreadStatic]
    static bool disabled;

    // Disables the converter in a thread-safe manner.
    bool Disabled { get { return disabled; } set { disabled = value; } }

    public override bool CanWrite { get { return !Disabled; } }

    public override bool CanRead { get { return !Disabled; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        using (new PushValue<bool>(true, () => Disabled, val => Disabled = val))
        using (new PushValue<PreserveReferencesHandling>(PreserveReferencesHandling.Objects, () => serializer.PreserveReferencesHandling, val => serializer.PreserveReferencesHandling = val))
        using (new PushValue<DefaultValueHandling>(DefaultValueHandling.Ignore, () => serializer.DefaultValueHandling, val => serializer.DefaultValueHandling = val))
        using (new PushValue<NullValueHandling>(NullValueHandling.Ignore, () => serializer.NullValueHandling, val => serializer.NullValueHandling = val))
        {
            serializer.Serialize(writer, value);
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }
        using (new PushValue<bool>(true, () => Disabled, val => Disabled = val))
        using (new PushValue<PreserveReferencesHandling>(PreserveReferencesHandling.Objects, () => serializer.PreserveReferencesHandling, val => serializer.PreserveReferencesHandling = val))
        using (new PushValue<DefaultValueHandling>(DefaultValueHandling.Ignore, () => serializer.DefaultValueHandling, val => serializer.DefaultValueHandling = val))
        using (new PushValue<NullValueHandling>(NullValueHandling.Ignore, () => serializer.NullValueHandling, val => serializer.NullValueHandling = val))
        {
            return serializer.Deserialize(reader, objectType);
        }
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}

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

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

请注意,禁用该转换器的逻辑需要进行线程安全的,因为Json.NET将分享跨线程合同和转换器。

演示小提琴#1 here

作为替代方案,可以完全消除转换器和直接应​​用到[JsonObject(IsReference = true)] ModelBase

[JsonObject(IsReference = true)]
public abstract class ModelBase : IModelBase
{
    public string ID { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime ModifiedAt { get; set; }
}

然后序列化与DefaultValueHandling.Ignore在像这样设置中指定NullValueHandling.Ignore反序列化:

static object Deserialize(Stream streamFromWebAPICall, Type objectType)
{
    using (var sr = new StreamReader(streamFromWebAPICall))
    {
        using (var jtr = new JsonTextReader(sr))
        {
            var settings = new JsonSerializerSettings
            {
                DefaultValueHandling = DefaultValueHandling.Ignore,
                NullValueHandling = NullValueHandling.Ignore,
            };
            var js = JsonSerializer.CreateDefault(settings);
            return js.Deserialize(jtr, objectType);
        }
    }
}

演示小提琴#2 here

请注意,您还可以设置[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]Json.NET 11.0.1的,但似乎没有要在ItemDefaultValueHandlingJsonObjectAttribute设置,因此增加,为串行设置(或使用nullables可选值类型的值,如CreatedAt)是必需的。

© www.soinside.com 2019 - 2024. All rights reserved.