c# – 合并两个XML文件并添加缺少的标记和属性

我有两个具有相同基本格式的 XML文件,但Master. XML中的一些标记属性未包含在Child.XML中.

我需要将XML文件合并到一个缺少标签属性的新XML文件中.

如果Master.XML和Child.XML中的值不同,则应使用Child.XML中的值.

我尝试将Union和Concat用于节点,但它不起作用.

master.DescendantNodes().Union(child.DescendantNodes());

任何建议都会有所帮助.

Master.XML

<SysConfig IsRuntime="False" BarcodeEnabled="false" version="1.2.0.0">
    <DbPath>C:\Agilent_i1000\ICPT_DB.sqlite</DbPath> 
 <CardDiagonsticsDelayTime>10</CardDiagonsticsDelayTime>   
    <ScreenSpecs NameID="CoreID" XrelativeID="X" YrelativeID="Y">
        <ScreenSpec Name="MainCtrlPanel" Xrelative="0" Yrelative="0" ></ScreenSpec>
        <ScreenSpec Name="1" Xrelative="75" Yrelative="0"  NottoUse="1"></ScreenSpec>
        <ScreenSpec Name="2" Xrelative="75" Yrelative="25"  NottoUse="1"></ScreenSpec>        
    </ScreenSpecs>  
</SysConfig>

Child.XML

<SysConfig IsRuntime="False" BarcodeEnabled="false" version="1.2.0.0">
<CardDiagonsticsDelayTime>20</CardDiagonsticsDelayTime>   
       <ScreenSpecs NameID="CoreID" XrelativeID="X" YrelativeID="Y">
        <ScreenSpec Name="MainCtrlPanel" Xrelative="0" Yrelative="0" ></ScreenSpec>
        <ScreenSpec Name="1" Xrelative="100" Yrelative="0" ></ScreenSpec>
        <ScreenSpec Name="2" Xrelative="75" Yrelative="25"></ScreenSpec> 
        <ScreenSpec Name="3" Xrelative="175" Yrelative="25"></ScreenSpec>        
    </ScreenSpecs>  
</SysConfig>

预期产出

<SysConfig IsRuntime="False" BarcodeEnabled="false" version="1.2.0.0">
    <DbPath>C:\Agilent_i1000\ICPT_DB.sqlite</DbPath> 
    <CardDiagonsticsDelayTime>20</CardDiagonsticsDelayTime>   
           <ScreenSpecs NameID="CoreID" XrelativeID="X" YrelativeID="Y">
            <ScreenSpec Name="MainCtrlPanel" Xrelative="0" Yrelative="0" ></ScreenSpec>
            <ScreenSpec Name="1" Xrelative="100" Yrelative="0" NottoUse="1" ></ScreenSpec>
            <ScreenSpec Name="2" Xrelative="75" Yrelative="25" NottoUse="1"></ScreenSpec>  
            <ScreenSpec Name="3" Xrelative="175" Yrelative="25">
</ScreenSpec>      
        </ScreenSpecs>  
    </SysConfig>

解决方法

以下是一组能够合并两个XML文档的实用程序类.整个过程非常通用,但您可能需要对其进行修改以满足其他特定需求.

以下是处理您案例的代码

var result = new XmlXPathDocument();
    result.Load("master.xml");

    var child = new XmlXPathDocument();
    child.Load("child.xml");

    // here we tell the class that "Name" is a discriminant attribute.
    // two nodes at the same level with the same discriminant attributes are considered the same,so will be merged
    // if we don't do this,they will considered different and they will be added if the set of their attributes is different
    child.AdddiscriminantAttribute("Name",string.Empty);
    child.InjectXml(result);

    result.Save("output.xml");

这些类派生自标准的XmlDocument类层次结构.它们使用自动计算的XPATH表达式,以便能够逐节点地合并文档.

public class XmlXPathDocument : XmlDocument
{
    public const string XmlNamespaceUri = "http://www.w3.org/2000/xmlns/";
    public const string XmlNamespacePrefix = "xmlns";

    internal List<Tuple<string,string>> _discriminantAttributes = new List<Tuple<string,string>>();

    public XmlXPathDocument() => Construct();
    public XmlXPathDocument(XmlNaMetable naMetable) : base(naMetable) => Construct();
    public XmlXPathDocument(XmlImplementation implementation) : base(implementation) => Construct();

    protected virtual void Construct() => XPathNamespaceManager = new XmlNamespaceManager(new NaMetable());

    public virtual XmlNamespaceManager XPathNamespaceManager { get; private set; }

    public override XmlElement CreateElement(string prefix,string localName,string namespaceURI) => new XmlXpathelement(prefix,localName,namespaceURI,this);

    public override XmlCDataSection CreateCDataSection(string data) => new XmlXPathCDataSection(data,this);

    public override XmlText CreateTextNode(string text) => new XmlXPathText(text,this);

    public virtual void AdddiscriminantAttribute(string name,string namespaceURI)
    {
        if (name == null)
            throw new ArgumentNullException(nameof(name));

        _discriminantAttributes.Add(new Tuple<string,string>(name,namespaceURI));
    }

    public virtual bool Isdiscriminant(XmlAttribute attribute)
    {
        if (attribute == null)
            throw new ArgumentNullException(nameof(attribute));

        foreach (var pair in _discriminantAttributes)
        {
            string ns = Nullify(attribute.NamespaceURI);
            string dns = Nullify(pair.Item2);
            if (ns == dns && pair.Item1 == attribute.LocalName)
                return true;
        }
        return false;
    }

    private static string Nullify(string text)
    {
        if (text == null)
            return null;

        text = text.Trim();
        if (text.Length == 0)
            return null;

        return text;
    }

    internal string GetPrefix(string namespaceURI)
    {
        if (string.IsNullOrEmpty(namespaceURI))
            return null;

        string prefix = XPathNamespaceManager.LookupPrefix(namespaceURI);
        if (!string.IsNullOrEmpty(prefix))
        {
            XPathNamespaceManager.AddNamespace(prefix,namespaceURI);
            return prefix;
        }

        string newPrefix;
        int index = 0;
        do
        {
            newPrefix = "ns" + index;
            if (XPathNamespaceManager.LookupNamespace(newPrefix) == null)
                break;

            index++;
        }
        while (true);
        XPathNamespaceManager.AddNamespace(newPrefix,namespaceURI);
        return newPrefix;
    }

    private static bool IsNamespaceAttribute(XmlAttribute attribute)
    {
        if (attribute == null)
            return false;

        return attribute.NamespaceURI == XmlNamespaceUri && attribute.Prefix == XmlNamespacePrefix;
    }

    private static IEnumerable<XmlAttribute> GetAttributes(IXmlXPathNode node)
    {
        var xe = node as XmlElement;
        if (xe == null)
            yield break;

        foreach (XmlAttribute att in xe.Attributes)
        {
            yield return att;
        }
    }

    private static XmlAttribute GetAttribute(IXmlXPathNode node,string name) => node is XmlElement xe ? xe.Attributes[name] : null;
    private static XmlAttribute GetAttribute(IXmlXPathNode node,string ns) => node is XmlElement xe ? xe.Attributes[localName,ns] : null;

    public virtual bool InjectXml(XmlDocument target)
    {
        if (target == null)
            throw new ArgumentNullException(nameof(target));

        if (DocumentElement == null)
            return false;

        bool changed = false;
        foreach (XmlNode node in SelectNodes("//node()"))
        {
            var xelement = node as IXmlXPathNode;
            if (xelement == null)
                continue;

            if (string.IsNullOrEmpty(xelement.XPathExpression))
                continue;

            XmlNode other = target.SelectSingleNode(xelement.XPathExpression,XPathNamespaceManager);
            if (other != null)
            {
                if (other is XmlElement otherElement)
                {
                    foreach (XmlAttribute att in GetAttributes(xelement))
                    {
                        if (IsNamespaceAttribute(att))
                            continue;

                        if (otherElement.Attributes[att.LocalName,att.NamespaceURI]?.Value != att.Value)
                        {
                            otherElement.SetAttribute(att.LocalName,att.NamespaceURI,att.Value);
                            changed = true;
                        }
                    }
                    continue;
                }
            }

            if (node is XmlXpathelement element)
            {
                XmlElement parent = EnsureTargetParent(xelement,target,out changed);
                XmlElement targetElement = target.CreateElement(element.LocalName,element.NamespaceURI);
                changed = true;
                if (parent == null)
                {
                    target.AppendChild(targetElement);
                }
                else
                {
                    parent.AppendChild(targetElement);
                }

                foreach (XmlAttribute att in GetAttributes(xelement))
                {
                    if (IsNamespaceAttribute(att))
                        continue;

                    targetElement.SetAttribute(att.LocalName,att.Value);
                }
                continue;
            }

            if (node is XmlXPathCDataSection cdata)
            {
                XmlElement parent = EnsureTargetParent(xelement,out changed);
                var targetCData = target.CreateCDataSection(cdata.Value);
                changed = true;
                if (parent == null)
                {
                    target.AppendChild(targetCData);
                    AppendNextTexts(node,targetCData,target);
                }
                else
                {
                    if (parent.ChildNodes.Count == 1 && parent.ChildNodes[0] is XmlCharacterData)
                    {
                        parent.RemoveChild(parent.ChildNodes[0]);
                    }
                    parent.AppendChild(targetCData);
                    AppendNextTexts(node,parent);
                }
                continue;
            }

            if (node is XmlXPathText text)
            {
                XmlElement parent = EnsureTargetParent(xelement,out changed);
                var targetText = target.CreateTextNode(text.Value);
                changed = true;
                if (parent == null)
                {
                    target.AppendChild(targetText);
                    AppendNextTexts(node,targetText,target);
                }
                else
                {
                    if (parent.ChildNodes.Count == 1 && parent.ChildNodes[0] is XmlCharacterData)
                    {
                        parent.RemoveChild(parent.ChildNodes[0]);
                    }
                    parent.AppendChild(targetText);
                    AppendNextTexts(node,parent);
                }
                continue;
            }
        }
        return changed;
    }

    private static void AppendNextTexts(XmlNode textNode,XmlNode targetTextNode,XmlNode parent)
    {
        do
        {
            if (textNode.NextSibling is XmlText text)
            {
                var newText = targetTextNode.OwnerDocument.CreateTextNode(text.Value);
                parent.AppendChild(newText);
            }
            else
            {
                var cdata = textNode.NextSibling as XmlCDataSection;
                if (cdata == null)
                    break;

                var newCData = targetTextNode.OwnerDocument.CreateCDataSection(cdata.Value);
                parent.AppendChild(newCData);
            }
            textNode = textNode.NextSibling;
        }
        while (true);
    }

    private static XmlElement EnsureTargetParent(IXmlXPathNode element,XmlDocument target,out bool changed)
    {
        changed = false;
        if (element.ParentNode is XmlXpathelement parent)
        {
            if (string.IsNullOrEmpty(parent.XPathExpression))
                return null;

            if (target.SelectSingleNode(parent.XPathExpression,element.OwnerDocument.XPathNamespaceManager) is XmlElement targetElement)
                return targetElement;

            var parentElement = EnsureTargetParent(parent,out changed);
            targetElement = target.CreateElement(parent.LocalName,parent.NamespaceURI);
            parentElement.AppendChild(targetElement);
            changed = true;
            return targetElement;
        }
        return target.DocumentElement;
    }
}

public class XmlXpathelement : XmlElement,IXmlXPathNode
{
    private Lazy<string> _xPathExpression;

    public XmlXpathelement(string prefix,string namespaceURI,XmlXPathDocument doc) : base(prefix,doc)
    {
        _xPathExpression = new Lazy<string>(() => GetXPathExpression());
    }

    public new XmlXPathDocument OwnerDocument => (XmlXPathDocument)base.OwnerDocument;
    public virtual string XPathExpression => _xPathExpression.Value;

    private static string GetAttEscapedValue(string value)
    {
        if (value.IndexOf('\'') >= 0)
            return "=\"" + value.Replace("\"","&quot;") + "\"";

        return "='" + value + "'";
    }

    private string GetdiscriminantAttributeXPath()
    {
        foreach (var att in OwnerDocument._discriminantAttributes)
        {
            XmlAttribute disc;
            if (string.IsNullOrEmpty(att.Item2))
            {
                disc = GetAttributeNode(att.Item1);
            }
            else
            {
                disc = GetAttributeNode(att.Item1,att.Item2);
            }

            if (disc != null)
            {
                string newPrefix = OwnerDocument.GetPrefix(NamespaceURI);
                string name = Name + "[@" + disc.Name + GetAttEscapedValue(disc.Value) + "]";
                if (newPrefix != null)
                {
                    name = newPrefix + ":" + name;
                }
                return name;
            }
        }
        return null;
    }

    private string GetAttributesXPath()
    {
        if (Attributes.Count == 0)
            return null;

        var sb = new StringBuilder();
        foreach (XmlAttribute att in Attributes)
        {
            if (sb.Length > 0)
            {
                sb.Append(" and ");
            }

            sb.Append("@");
            sb.Append(att.Name);
            sb.Append(GetAttEscapedValue(att.Value));
            OwnerDocument.GetPrefix(att.NamespaceURI);
        }

        var text = sb.ToString().Trim();
        if (text.Length == 0)
            return null;

        return "[" + text + "]";
    }

    private string GetXPath(XmlNodeList parentNodes)
    {
        string discriminant = GetdiscriminantAttributeXPath();
        if (discriminant != null)
            return discriminant;

        string name = Name;
        string newPrefix = OwnerDocument.GetPrefix(NamespaceURI);
        if (newPrefix != null)
        {
            name = newPrefix + ":" + LocalName;
        }

        if (parentNodes.Count == 1)
            return name;

        var sameName = new List<XmlElement>();
        foreach (XmlNode node in parentNodes)
        {
            if (node.NodeType != XmlNodeType.Element)
                continue;

            if (node.Name == Name)
            {
                sameName.Add((XmlElement)node);
            }
        }

        if (sameName.Count == 1)
            return name;

        string byIndex = null;
        var sameAtts = new List<XmlElement>();
        for (int i = 0; i < sameName.Count; i++)
        {
            if (sameName[i] == this)
            {
                byIndex = name + "[" + (i + 1) + "]";
                continue;
            }

            bool same = true;
            foreach (XmlAttribute att in Attributes)
            {
                XmlAttribute sameAtt = sameName[i].Attributes[att.LocalName,att.NamespaceURI];
                if (sameAtt == null || string.Compare(sameAtt.Value,att.Value,StringComparison.OrdinalIgnoreCase) != 0)
                {
                    same = false;
                    break;
                }
            }

            if (same)
            {
                sameAtts.Add(sameName[i]);
            }
        }

        if (sameAtts.Count == 0)
            return name + GetAttributesXPath();

        return byIndex;
    }

    private string GetXPathExpression()
    {
        if (ParentNode == null)
        {
            string name = Name;
            string newPrefix = OwnerDocument.GetPrefix(NamespaceURI);
            if (newPrefix != null)
            {
                name = newPrefix + ":" + name;
            }
            return name;
        }

        string expr = GetXPath(ParentNode.ChildNodes);
        if (ParentNode is XmlXpathelement parent)
        {
            expr = parent.XPathExpression + "/" + expr;
        }

        if (ParentNode.NodeType == XmlNodeType.Document)
        {
            expr = "/" + expr;
        }
        return expr;
    }
}

public class XmlXPathText : XmlText,IXmlXPathNode
{
    private Lazy<string> _xPathExpression;

    public XmlXPathText(string data,XmlXPathDocument doc) : base(data,doc)
    {
        _xPathExpression = new Lazy<string>(() => GettextxPathExpression(this));
    }

    public new XmlXPathDocument OwnerDocument => (XmlXPathDocument)base.OwnerDocument;
    public virtual string XPathExpression => _xPathExpression.Value;

    internal static string GettextxPathExpression(XmlNode node)
    {
        if (node.ParentNode is IXmlXPathNode element)
            return element.XPathExpression + "/text()";

        return null;
    }
}

public class XmlXPathCDataSection : XmlCDataSection,IXmlXPathNode
{
    private Lazy<string> _xPathExpression;

    public XmlXPathCDataSection(string data,doc)
    {
        _xPathExpression = new Lazy<string>(() => XmlXPathText.GettextxPathExpression(this));
    }

    public new XmlXPathDocument OwnerDocument => (XmlXPathDocument)base.OwnerDocument;
    public virtual string XPathExpression => _xPathExpression.Value;
}

public interface IXmlXPathNode
{
    string XPathExpression { get; }
    XmlNode ParentNode { get; }
    XmlXPathDocument OwnerDocument { get; }
}

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...