在JSON.NET如何获得对每个反序列化对象的引用



我正在尝试使用JSON.NET实现idesseriizationcallback。我在反序列化一个对象,我想生成一个列表所有被反序列化的对象它们实现了idesseriizationcallback,最好的方法是什么?JSON。. NET有任何适当的扩展点来促进这一点吗?我有一个(看似)工作的解决方案,但它是相当丑陋的,所以我相信一定有一个更好的方法来做到这一点。任何帮助都很感激,谢谢!

    private static JsonSerializer serializer = new JsonSerializer();
    static cctor()
    {
        serializer.Converters.Add(new DeserializationCallbackConverter());
    }
    public static T Deserialize<T>(byte[] data)
    {
        using (var reader = new JsonTextReader(new StreamReader(new MemoryStream(data))))
        using (DeserializationCallbackConverter.NewDeserializationCallbackBlock(reader))
            return serializer.Deserialize<T>(reader);
    }
    private class DeserializationCallbackConverter : JsonConverter
    {
        [ThreadStatic]
        private static ScopedConverter currentConverter;
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return currentConverter.ReadJson(reader, objectType, serializer);
        }
        public override bool CanConvert(Type objectType)
        {
            return currentConverter == null ? false : currentConverter.CanConvert();
        }
        public override bool CanWrite
        {
            get { return false; }
        }
        public static IDisposable NewDeserializationCallbackBlock(JsonReader reader)
        {
            return new ScopedConverter(reader);
        }
        private class ScopedConverter : IDisposable
        {
            private JsonReader jsonReader;
            private string currentPath;
            private List<IDeserializationCallback> callbackObjects;
            public ScopedConverter(JsonReader reader)
            {
                jsonReader = reader;
                callbackObjects = new List<IDeserializationCallback>();
                currentConverter = this;
            }
            public object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer)
            {
                var lastPath = currentPath;
                currentPath = reader.Path;
                var obj = serializer.Deserialize(reader, objectType);
                currentPath = lastPath;
                var dc = obj as IDeserializationCallback;
                if (dc != null && callbackObjects != null)
                    callbackObjects.Add(dc);
                return obj;
            }
            public bool CanConvert()
            {
                return jsonReader.Path != currentPath;
            }
            public void Dispose()
            {
                currentConverter = null;
                foreach (var obj in callbackObjects)
                    obj.OnDeserialization(null);
            }
        }
    }

您可以创建一个自定义契约解析器,它添加了一个额外的人工OnDeserialized回调,用于跟踪引用类型对象的创建。下面是一个例子:

public interface IObjectCreationTracker
{
    void Add(object obj);
    ICollection<object> CreatedObjects { get; }
}
public class ReferenceObjectCreationTracker : IObjectCreationTracker
{
    public ReferenceObjectCreationTracker()
    {
        this.CreatedObjects = new HashSet<object>();
    }
    public void Add(object obj)
    {
        if (obj == null)
            return;
        var type = obj.GetType();
        if (type.IsValueType || type == typeof(string))
            return;
        CreatedObjects.Add(obj);
    }
    public ICollection<object> CreatedObjects { get; private set; }
}
public class ObjectCreationTrackerContractResolver : DefaultContractResolver
{
    readonly SerializationCallback callback = (o, context) =>
        {
            var tracker = context.Context as IObjectCreationTracker;
            if (tracker != null)
                tracker.Add(o);
        };
    protected override JsonContract CreateContract(Type objectType)
    {
        var contract = base.CreateContract(objectType);
        contract.OnDeserializedCallbacks.Add(callback);
        return contract;
    }
}

然后按如下方式使用:

public static class JsonExtensions
{
    public static T DeserializeWithTracking<T>(string json, out ICollection<object> objects)
    {
        var tracker = new ReferenceObjectCreationTracker();
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new ObjectCreationTrackerContractResolver(),
            Context = new StreamingContext(StreamingContextStates.All, tracker),
            // Add other settings as required.  
            TypeNameHandling = TypeNameHandling.Auto, 
        };
        var obj = (T)JsonConvert.DeserializeObject<T>(json, settings);
        objects = tracker.CreatedObjects;
        return obj;
    }
}

注意,这只返回非string 引用类型的实例。返回值类型的实例更有问题,因为没有明显的方法来区分最终通过属性设置器嵌入到更大对象中的值类型和作为盒装引用保留在对象图中的值类型,例如在这个问题中所示。如果盒装值类型最终嵌入到某个更大的对象中,则无法保留对它的直接引用。

还注意使用StreamingContext.Context将跟踪器向下传递到回调。

您可能希望缓存契约解析器以获得最佳性能。

在回答如何用Json实现IDeserializationCallback的更新问题。. NET中,以上应该适用于引用类型。对于实现此接口的值类型,您可以:

  1. OnDeserialized回调中立即调用该方法,而不是将其延迟到序列化完成,或者

  2. 抛出一个异常,指出IDeserializationCallback不支持结构

最新更新