Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Newtonsoft Json.NET JsonConverter属性保留反序列化时的引用问题_C#_Serialization_Json.net_Deserialization_Json Deserialization - Fatal编程技术网

C# Newtonsoft Json.NET JsonConverter属性保留反序列化时的引用问题

C# Newtonsoft Json.NET JsonConverter属性保留反序列化时的引用问题,c#,serialization,json.net,deserialization,json-deserialization,C#,Serialization,Json.net,Deserialization,Json Deserialization,在项目的模型中,我使用一个JsonConverter属性来帮助(反)序列化这些模型 转换器当前看起来如下所示: public class CustomJsonConverter : Newtonsoft.Json.JsonConverter { bool _canWrite = true; public override bool CanWrite { get { return _canWrite; } } public override

在项目的模型中,我使用一个
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; }
}
这些模型的基类如下所示:

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.NETWebAPI2应用程序(服务器端)和C#应用程序(客户端)中使用这些模型。对于大多数API调用,将返回一个模型数组。序列化模型时,一切都按预期进行。但是,在反序列化时,只有每个引用的第一次出现时才会填充信息

例如: 尝试反序列化web API返回的此JSON对象时,第一个
CustomerModel
对象的
CreatedBy
ModifiedBy
属性将是正确的。但是,对于第二个
CustomerModel
对象,这些属性将是未设置任何属性的新
UserModel
实例

为了反序列化JSON字符串,我使用以下代码:

using (var sr = new StreamReader(streamFromWebAPICall))
{                
    using (var jtr = new JsonTextReader(sr))
    {
        var js = new JsonSerializer();
        return js.Deserialize(jtr, objectType);
    }
}
如何正确设置所有反序列化对象的属性

编辑:
问题似乎出现在
序列化程序.Populate(reader,existingValue)
中,其中的引用不被记住。

您的基本问题是,您正在为您还希望启用的类型提供。但无论何时应用自定义转换器,它都必须手动处理一切,包括解析和生成
“$ref”
“$id”
属性。转换器不会执行此操作,因此反序列化代码不会正确反序列化对象图

to包含一个模板转换器,该转换器显示如何处理这些属性。但是,由于转换器似乎只是在(反)序列化之前切换一些序列化程序设置,因此您可以简单地调用递归(反)序列化对象,在期间禁用转换器,如下所示:

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);
    }
}
然后使用
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


注意:您也可以设置为,但
JsonObjectAttribute
上似乎没有
ItemDefaultValueHandling
设置,因此需要将其添加到序列化程序设置中(或对可选值类型值(如
CreatedAt
)使用nullables),但是您可能需要在转换器本身内部手动检查
$ref
$id
属性,如中所示,您能否解释一下您在
CustomJsonConverter
转换器中尝试执行的操作?是为了递归地为给定类型的对象和所有子体启用某些序列化程序选项吗?我确实只是尝试为web API返回的每个序列化模型应用某些序列化程序设置。您的自定义
JsonConverter
版本确实解决了我的问题。另一种选择并没有真正的帮助,因为您在客户端添加了序列化程序设置,而不是在web API中。在发表我的原始帖子之后,我还发现了
[JsonObject(ItemNullValueHandling=NullValueHandling.Ignore)]
属性,但正如您所说,使用这种方法无法设置
ItemDefaultValueHandling
。最终我决定使用我发现的另一种方法。也就是说,只需在我的web API的
Global.asax.cs
文件的
Application\u Start()
中应用
SerializerSettings