Merge two XElements

前端 未结 2 1591
醉酒成梦
醉酒成梦 2021-01-14 06:36

I\'m not quite sure how to ask this, or if this even exists, but I have a need to merge two XElements with one taking precendence over the other, to become just one element.

2条回答
  •  刺人心
    刺人心 (楼主)
    2021-01-14 06:45

    For the sake of others looking for the same thing, as I assume both the people contributing have long since lost interest... I needed to do something similar but a little more complete. Still not totally complete though, as the XMLDoc says it does not handle non-element content well, but I don't need to as my non-element content is either text or unimportant. Feel free to enhance and re-post... Oh and it's C# 4.0 as that's what I use...

    /// 
    /// Provides facilities to merge 2 XElement or XML files. 
    /// 
    /// Where the LHS holds an element with non-element content and the RHS holds 
    /// a tree, the LHS non-element content will be applied as text and the RHS 
    /// tree ignored. 
    /// 
    /// 
    /// This does not handle anything other than element and text nodes (infact 
    /// anything other than element is treated as text). Thus comments in the 
    /// source XML are likely to be lost.
    /// 
    /// You can pass  if it you have XDocs 
    /// to work with:
    /// 
    /// XDocument mergedDoc = new XDocument(MergeElements(lhsDoc.Root, rhsDoc.Root);
    /// 
    /// 
    public class XmlMerging
    {
        /// 
        /// Produce an XML file that is made up of the unique data from both
        /// the LHS file and the RHS file. Where there are duplicates the LHS will 
        /// be treated as master
        /// 
        /// XML file to base the merge off. This will override 
        /// the RHS where there are clashes
        /// XML file to enrich the merge with
        /// The fully qualified file name in which to 
        /// write the resulting merged XML
        ///  Specifies the options to apply when saving. 
        /// Default is 
        public static bool TryMergeXmlFiles(string lhsPath, string rhsPath, 
            string resultPath, SaveOptions options = SaveOptions.OmitDuplicateNamespaces)
        {
            try
            {
                MergeXmlFiles(lhsPath, rhsPath, resultPath);
            }
            catch (Exception)
            {
                // could integrate your logging here
                return false;
            }
            return true;
        }
    
        /// 
        /// Produce an XML file that is made up of the unique data from both the LHS
        /// file and the RHS file. Where there are duplicates the LHS will be treated 
        /// as master
        /// 
        /// XML file to base the merge off. This will override 
        /// the RHS where there are clashes
        /// XML file to enrich the merge with
        /// The fully qualified file name in which to write 
        /// the resulting merged XML
        ///  Specifies the options to apply when saving. 
        /// Default is 
        public static void MergeXmlFiles(string lhsPath, string rhsPath, 
            string resultPath, SaveOptions options = SaveOptions.OmitDuplicateNamespaces)
        {
            XElement result = 
                MergeElements(XElement.Load(lhsPath), XElement.Load(rhsPath));
            result.Save(resultPath, options);
        }
    
        /// 
        /// Produce a resulting  that is made up of the unique 
        /// data from both the LHS element and the RHS element. Where there are 
        /// duplicates the LHS will be treated as master
        /// 
        /// XML Element tree to base the merge off. This will 
        /// override the RHS where there are clashes
        /// XML element tree to enrich the merge with
        /// A merge of the left hand side and right hand side element 
        /// trees treating the LHS as master in conflicts
        public static XElement MergeElements(XElement lhs, XElement rhs)
        {
            // if either of the sides of the merge are empty then return the other... 
            // if they both are then we return null
            if (rhs == null) return lhs;
            if (lhs == null) return rhs;
    
            // Otherwise build a new result based on the root of the lhs (again lhs 
            // is taken as master)
            XElement result = new XElement(lhs.Name);
    
            MergeAttributes(result, lhs.Attributes(), rhs.Attributes());
    
            // now add the lhs child elements merged to the RHS elements if there are any
            MergeSubElements(result, lhs, rhs);
            return result;
        }
    
        /// 
        /// Enrich the passed in  with the contents of both 
        /// attribute collections.
        /// Again where the RHS conflicts with the LHS, the LHS is deemed the master
        /// 
        /// The element to take the merged attribute 
        /// collection
        /// The master set of attributes
        /// The attributes to enrich the merge
        private static void MergeAttributes(XElement elementToUpdate, 
            IEnumerable lhs, IEnumerable rhs)
        {
            // Add in the attribs of the lhs... we will only add new attribs from 
            // the rhs duplicates will be ignored as lhs is master
            elementToUpdate.Add(lhs);
    
            // collapse the element names to save multiple evaluations... also why 
            // we ain't putting this in as a sub-query
            List lhsAttributeNames = 
                lhs.Select(attribute => attribute.Name).ToList();
            // so add in any missing attributes
            elementToUpdate.Add(rhs.Where(attribute => 
                !lhsAttributeNames.Contains(attribute.Name)));
        }
    
        /// 
        /// Enrich the passed in  with the contents of both 
        ///  subtrees.
        /// Again where the RHS conflicts with the LHS, the LHS is deemed the master.
        /// Where the passed elements do not have element subtrees, but do have text 
        /// content that will be used. Again the LHS will dominate
        /// 
        /// Where the LHS has text content and no subtree, but the RHS has 
        /// a subtree; the LHS text content will be used and the RHS tree ignored. 
        /// This may be unexpected but is consistent with other .NET XML 
        /// operations
        /// The element to take the merged element 
        /// collection
        /// The element from which to extract the master 
        /// subtree
        /// The element from which to extract the subtree to 
        /// enrich the merge
        private static void MergeSubElements(XElement elementToUpdate, 
            XElement lhs, XElement rhs)
        {
            // see below for the special case where there are no children on the LHS
            if (lhs.Elements().Count() > 0)
            {
                // collapse the element names to a list to save multiple evaluations...
                // also why we ain't putting this in as a sub-query later
                List lhsElementNames = 
                    lhs.Elements().Select(element => element.Name).ToList();
    
                // Add in the elements of the lhs and merge in any elements of the 
                //same name on the RHS
                elementToUpdate.Add(
                    lhs.Elements().Select(
                        lhsElement => 
                            MergeElements(lhsElement, rhs.Element(lhsElement.Name))));
    
                // so add in any missing elements from the rhs
                elementToUpdate.Add(rhs.Elements().Where(element => 
                    !lhsElementNames.Contains(element.Name)));
            }
            else
            {
                // special case for elements where they have no element children 
                // but still have content:
                // use the lhs text value if it is there
                if (!string.IsNullOrEmpty(lhs.Value))
                {
                    elementToUpdate.Value = lhs.Value;
                }
                // if it isn't then see if we have any children on the right
                else if (rhs.Elements().Count() > 0)
                {
                    // we do so shove them in the result unaltered
                    elementToUpdate.Add(rhs.Elements());
                }
                else
                {
                    // nope then use the text value (doen't matter if it is empty 
                    //as we have nothing better elsewhere)
                    elementToUpdate.Value = rhs.Value;
                }
            }
        }
    }
    

提交回复
热议问题