• XmlDocument 解决 Clone、CloneNode、ImportNode 等节点克隆后的标签自闭合问题


    前言:

    这两天在对 Taurus.Mvc 做 html 加载性能优化时,发现存在这个问题。

    具体优化的是 CYQ.Data 组件的 XHtmlAction 相关类。

    问题过程:

    之前 XmlDocument 调用 LoadXml(xml)之后,缓存对象,再次使用时,都是重新LoadXml:

    复制代码
                XmlDocument newDoc = new XmlDocument();
                try
                {
                    newDoc.LoadXml(缓存xDoc.InnerXml);
                }
                catch (Exception err)
                {
                    Log.Write(err, LogType.Error);
                    newDoc.InnerXml = xDoc.InnerXml;
                }
                return newDoc;
    复制代码

    上面的代码,多年来一直工作很好,直到最近,在做优化,发现还原节点,可以通过 Clone 方式,而且性能提升不少:

    代码如:

     //克隆速度快。
    return 缓存xDoc.Clone() as XmlDocument;

    后面发现用 CloneNode 方式,还能更优一点:

     //克隆速度快。
    return 缓存xDoc.CloneNode(true) as XmlDocument;

    以为问题解决时,却出现了样式怪异的问题,一开始以为是克隆还保持引用的问题,花了不少时间,找错了方向。

    经反复排查,对比生成的内容,才发现以上的方法,都会造成 div 空节点标签被自闭合了:

    成了:

    经测试,确认是该问题(仅.NET版本出问题,.NET Core 在以上方法,不会产生自闭合问题)。

    解决问题:

    要解决这个问题,网上找找解决方案:

    意外发现还有 ImportNode 方式可以用,尝试了一下,似乎更优一丁点,蚊子腿也是肉:

    HtmlDocument document = new HtmlDocument();
    XmlNode node = document.ImportNode(缓存xDoc.DocumentElement, true);
    document.AppendChild(node);
    return document;

    但,网上只有个别提问题,没有解决方案。

    但发现了标签是否闭合和以下属性有关:

    设置 XmlElement 的 IsEmpty = false;//不自闭合标签

    而 Clone 或 ImportNode,没有提供参数设置该参数。

    没思路的时候,就多看看.Net 源码。

     

    于是,想到通过继承:XmlDocument,改写 CreateElement 方法,来实现:

    复制代码
    class XHtmlDocument : XmlDocument
    {
        public override XmlElement CreateElement(string prefix, string localName, string namespaceURI)
        {
            XmlElement xe = base.CreateElement(prefix, localName, namespaceURI);
            switch (localName)
            {
                case "meta":
                case "br":
                case "hr":
                case "img":
                case "link":
                case "base":
                case "area":
                case "input":
                case "source":
                case "!DOCTYPE":
                    break;
                default:
                    xe.IsEmpty = false;//不自闭合标签
                    break;
            }
            
            return xe;
        }
    }
    复制代码

    总结:

    Xml 需要标签不自闭合的场景,可能很少,但若需要,这就是个解决方案之一。

    再补充 CYQ.Data 关于加载 html 的性能优化说明:

    复制代码
    /*
     * 性能优化说明:
     * 1、DTD:如果界面没有实体&xxx;则不加载,否则修改DTD路径到本地。
     * 2、XmlDocument:LoadXml 性能优化:加载后缓存,后续从缓存转化:这里要转化也没那么简单,需要解决以下问题:
     * ---------------------------------------------------------------------------------------
     * A、从缓存拿到,从缓存Doc拿出InnerXml,再重新LoadXml(xml),兼容性最好,但可优化。
     * B、从缓存拿到,从缓存Doc调用:Clone、CloneNode(true)、两个性能差不多,节点多时,后面那个更优。
     * C、从缓存拿到,从缓存Doc发现:XmlDocument ImportNode 方法比CloneNode(true) 更优,准备采用这个。
     * D、上面B、C两种方式,产生新的问题:.NET 下标签自闭合、.NET Core下正常,改动见:GetCloneFrom 方法。
     * E、解决标签自闭合问题:继承XmlDocument,改写CreateElement,根据W3C标准找出需要自闭合的,其余条件设置XmlElement的IsEmpty=false,见:XhtmlDocument
     * F、解决上述问题后,为了性能,从缓存中不加载DTD、引发样式问题:
     * G、解决F的问题是,输出的时候检测没有DTD头,则追加:头,见:XHtmlAction OutXml输出。
     * ----------------------------------------------------------------------------------------
     * 4、避免使用 InnerXml 属性,用其它方式替代:InnerText、节点引用等,但引发另一个问题,赋值text,则后续无法通过节点操作,这在循环绑定时会拿不到节点。
     */
    复制代码

     

  • 相关阅读:
    【WSN】基于蚁群算法的WSN路由协议(最短路径)消耗节点能量研究(Matlab代码实现)
    YModem协议总结
    大数据学习(9)-hadoop集群计算速度影响因素
    (Java实习生)每日10道面试题打卡——Java基础知识篇
    深入浅出学习透析Nginx服务器的基本原理和配置指南「Keepalive性能分析实战篇」
    linux下usleep函数对CPU占用率的影响
    【c++&GDAL】均值滤波+中值滤波
    基于LMI的非线性混沌系统滑模控制
    nodejs+vue装修公司CRM系统设计elementui
    某公司常见题刷题笔记
  • 原文地址:https://www.cnblogs.com/cyq1162/p/18003665