首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >相当于IDeserializationCallback.OnDeserialization的XAML阅读器

相当于IDeserializationCallback.OnDeserialization的XAML阅读器
EN

Stack Overflow用户
提问于 2019-03-11 22:42:48
回答 1查看 92关注 0票数 0

我已经向持久化类添加了新字段,并且需要确保在从磁盘加载旧版本的XAML序列化文件时将它们设置为合理的默认值。以前,在使用BinaryFormatter时,我会使用OnDeserialization方法来确定在持久化类中添加了新字段时应该设置哪些默认值(使用OptionalField属性)。例如:

代码语言:javascript
复制
    /// <summary>
    /// Runs when the entire object graph has been deserialized.
    /// </summary>
    /// <param name="sender">The object that initiated the callback. The functionality for this parameter is not currently implemented.</param>
    public override void
    OnDeserialization
        (object sender)
    {

到目前为止,在写入XAML文件时,我似乎找不到任何等效的东西,例如:

代码语言:javascript
复制
            using (TextReader reader = File.OpenText(filePath))
            {
                protocol = (Protocol)XamlServices.Load(reader);
            }

我希望确保在协议类型中不包含新的可选字段的旧文件(在上面的示例代码中)具有合理的默认值。我到处寻找,但似乎找不到任何明显的东西(例如https://ludovic.chabant.com/devblog/2008/06/25/almost-everything-you-need-to-know-about-xaml-serialization-part-2/),有没有类似的东西?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-11 23:32:55

XamlServices在内部使用XamlObjectWriter。此类型有一个包含各种回调的XamlObjectWriterSettings参数。这些都不是XamlServices公开的,但是它的功能很容易被复制。

我没有对此进行过广泛的测试,但这似乎是有效的:

代码语言:javascript
复制
public static object LoadXaml(TextReader textReader)
{
    var settings = new XamlObjectWriterSettings
    {
        AfterBeginInitHandler = (s, e) => Debug.Print($"Before deserializing {e.Instance}"),
        AfterEndInitHandler = (s, e) => Debug.Print($"After deserializing {e.Instance}")
    };

    using (var xmlReader = XmlReader.Create(textReader))
    using (var xamlReader = new XamlXmlReader(xmlReader))
    using (var xamlWriter = new XamlObjectWriter(xamlReader.SchemaContext, settings))
    {
        XamlServices.Transform(xamlReader, xamlWriter);
        return xamlWriter.Result;
    }
}

e.Instance包含被反序列化的对象。不确定哪个回调最适合您的目的。它们更等同于[OnDeserializing]/[OnDeserialized]属性,因为它们是在单个对象反序列化时调用的,而不是像IDeserializationCallback.OnDeserialization那样在整个图完成之后调用。

下面是一个在序列化期间提供事件的类的更完整的实现。XamlObjectReader不像XamlObjectWriter那样支持回调,所以这使用了一种变通方法。由于注释中解释的原因,它只在序列化对象之前而不是在序列化对象之后引发事件。

代码语言:javascript
复制
public class CallbackXamlService
{
    // Default settings that XamlService uses
    public XmlWriterSettings XmlWriterSettings { get; set; }
        = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true };

    public event EventHandler<XamlObjectEventArgs> BeforeDeserializing;
    public event EventHandler<XamlObjectEventArgs> AfterDeserializing;
    public event EventHandler<XamlObjectEventArgs> BeforeSerializing;
    // AfterSerializing event doesn't seem to be easily possible, see below

    public object LoadXaml(TextReader textReader)
    {
        var settings = new XamlObjectWriterSettings
        {
            BeforePropertiesHandler = (s, e) => BeforeDeserializing?.Invoke(this, e),
            AfterPropertiesHandler = (s, e) => AfterDeserializing?.Invoke(this, e)
        };

        using (var xmlReader = XmlReader.Create(textReader))
        using (var xamlReader = new XamlXmlReader(xmlReader))
        using (var xamlWriter = new XamlObjectWriter(xamlReader.SchemaContext, settings))
        {
            XamlServices.Transform(xamlReader, xamlWriter);
            return xamlWriter.Result;
        }
    }

    public string SaveXaml(object instance)
    {
        var stringBuilder = new StringBuilder();
        using (var textWriter = new StringWriter(stringBuilder))
            SaveXaml(textWriter, instance);
        return stringBuilder.ToString();
    }

    public void SaveXaml(TextWriter textWriter, object instance)
    {
        Action<object> beforeSerializing = (obj) => BeforeSerializing?.Invoke(this, new XamlObjectEventArgs(obj));

        // There are no equivalent callbacks on XamlObjectReaderSettings
        // Using a derived XamlObjectReader to track processed objects instead
        using (var xmlWriter = XmlWriter.Create(textWriter, XmlWriterSettings))
        using (var xamlXmlWriter = new XamlXmlWriter(xmlWriter, new XamlSchemaContext()))
        using (var xamlObjectReader = new CallbackXamlObjectReader(instance, xamlXmlWriter.SchemaContext, null, beforeSerializing))
        {
            XamlServices.Transform(xamlObjectReader, xamlXmlWriter);
            xmlWriter.Flush();
        }
    }

    private class CallbackXamlObjectReader : XamlObjectReader
    {
        public Action<object> BeforeSerializing { get; }

        //private Stack<object> instanceStack = new Stack<object>();

        public CallbackXamlObjectReader(object instance, XamlSchemaContext schemaContext, XamlObjectReaderSettings settings, Action<object> beforeSerializing)
            : base(instance, schemaContext, settings)
        {
            BeforeSerializing = beforeSerializing;
        }

        public override bool Read()
        {
            if (base.Read())
            {
                if (NodeType == XamlNodeType.StartObject)
                {
                    //instanceStack.Push(Instance);
                    BeforeSerializing?.Invoke(Instance);
                }
                // XamlObjectReader.Instance is not set on EndObject nodes
                // EndObject nodes do not line up with StartObject nodes when types like arrays and dictionaries
                // are involved, so using a stack to track the current instance doesn't always work.
                // Don't know if there is a reliable way to fix this without possibly fragile special-casing,
                // the XamlObjectReader internals are horrendously complex.
                //else if (NodeType == XamlNodeType.EndObject)
                //{
                //    object instance = instanceStack.Pop();
                //    AfterSerializing(instance);
                //}
                return true;
            }
            return false;
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55104374

复制
相关文章

相似问题

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