首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在JSON.NET中,如何获得对每个反序列化对象的引用?

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

Stack Overflow用户
提问于 2016-09-02 20:07:42
回答 1查看 845关注 0票数 1

我正在尝试使用IDeserializationCallback实现JSON.NET,我正在反序列化一个对象,我想生成一个清单,列出所有反序列化的对象,这些对象实现了IDeserializationCallback,那么最好的方法是什么呢?JSON.NET是否有适当的扩展点来促进这一点?下面我有一个(表面上)可行的解决方案,但是它很难看,所以我确信一定有更好的方法来做到这一点。任何帮助都是感激的,谢谢!

代码语言:javascript
复制
    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);
            }
        }
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-09-02 22:19:27

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

代码语言:javascript
复制
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;
    }
}

然后按以下方式使用:

代码语言:javascript
复制
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;
    }
}

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

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

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

更新

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

  1. OnDeserialized回调中立即调用该方法,而不是将其推迟到序列化完成,或
  2. 引发一个异常,该异常指示结构不支持IDeserializationCallback
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39300005

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档