I have two XML, before and after the user has edited them. I need to check that user have only added new elements but have not deleted or changed old ones.
Can anybody suggest to me a good algorithm to do that comparison?
Ps:
My XML has a very trivial schema, they only represent an object's structure (with nested objects) in a naive way.
There are few allowed tags, <object> tag can only contains <name> tag, <type> tag or a <list> tag.
The <name> and <type> tag can only contain a string; <list> tag instead can contain a <name> tag and a single <object> tags (representing the structure of objects in the list).
The string in the <name> tag can be freely choosen, the string in <type> tag instead can be only "string" , "int" , "float" , "bool" , "date" or "composite".
Here an example :
<object>
<name>Person</name>
<type>composite</type>
<object>
<name>Person_Name</name>
<type>string</type>
</object>
<object>
<name>Person_Surname</name>
<type>string</type>
</object>
<object>
<name>Person_Age</name>
<type>int</type>
</object>
<object>
<name>Person_Weight</name>
<type>float</type>
</object>
<object>
<name>Person_Address</name>
<type>string</type>
</object>
<object>
<name>Person_BirthDate</name>
<type>date</type>
</object>
<list>
<name>Person_PhoneNumbers</name>
<object>
<name>Person_PhoneNumber</name>
<type>composite</type>
<object>
<name>Person_PhoneNumber_ProfileName</name>
<type>string</type>
</object>
<object>
<name>Person_PhoneNumber_CellNumber</name>
<type>string</type>
</object>
<object>
<name>Person_PhoneNumber_HomeNumber</name>
<type>string</type>
</object>
<object>
<name>Person_PhoneNumber_FaxNumber</name>
<type>string</type>
</object>
<object>
<name>Person_PhoneNumber_Mail</name>
<type>string</type>
</object>
<object>
<name>Person_PhoneNumber_Social</name>
<type>string</type>
</object>
<object>
<name>Person_PhoneNumber_IsActive</name>
<type>bool</type>
</object>
</object>
</list>
</object>
You said:
I need to check that user have only added new elements
but have not deleted or changed old ones.
Can you be more precise about what you mean?
For example, if I insert a new "object" element somewhere, I've changed every element it's inside of, right? As many lists and other objects as contain it. In fact, any insertion at all is a change to the root element.
So, presumably you want to not count changes that change nothing but the root element. How about adding a new item to the list you show? Do you want the list to count as changed? Or what if the objects in the list, or the list itself, are moved to new places without having their content changed at all?
Each of those possibilities is pretty easy to write, but one has to decide what counts as a change first.
If, for example, you only care about bottom-level objects, and "the same" means precisely the same text content (no attributes, white-space variations, etc. etc.), then the easiest way is to load the "before" file into a list of (name,type) pairs; then load the "after" file into a similar but separate list. Sort both lists, then run down them simultaneously and report anything in the new one that's not in the old one (you'll probably want to report any deletions too, just in case).
I need to check that user have only added new elements but have not deleted or changed old ones.
You can represent your 2 XML files as objects. Traverse the nodes, get child the element count for each node and check if its child nodes exists on the other file. For comparing 2 complex objects, you can use the IEquatable.Equals() interface method. Read it here.
The code below doesn't care about the structure of your XML document or on which position a particular element exists since each element is represented as an XElement object. All it knows is 1.) the name of the element, 2.) that each element has children or not, 3.) has attributes or not, 4.) has innerxml or not, etc. If you want to be strict about the structure of your XML, you can represent each level as a single class.
public class Program
{
static void Main(string[] args)
{
XDocument xdoc1 = XDocument.Load("file1.xml");
XDocument xdoc2 = XDocument.Load("file2.xml");
RootElement file1 = new RootElement(xdoc1.Elements().First());
RootElement file2 = new RootElement(xdoc2.Elements().First());
bool isEqual = file1.Equals(file2);
Console.ReadLine();
}
}
public abstract class ElementBase<T>
{
public string Name;
public List<T> ChildElements;
public ElementBase(XElement xElement)
{
}
}
public class RootElement : ElementBase<ChildElement>, IEquatable<RootElement>
{
public RootElement(XElement xElement)
: base(xElement)
{
ChildElements = new List<ChildElement>();
Name = xElement.Name.ToString();
foreach (XElement e in xElement.Elements())
{
ChildElements.Add(new ChildElement(e));
}
}
public bool Equals(RootElement other)
{
bool flag = true;
if (this.ChildElements.Count != other.ChildElements.Count())
{
//--Your error handling logic here
flag = false;
}
List<ChildElement> otherChildElements = other.ChildElements;
foreach (ChildElement c in this.ChildElements)
{
ChildElement otherElement = otherChildElements.FirstOrDefault(x => x.Name == c.Name);
if (otherElement == null)
{
//--Your error handling logic here
flag = false;
}
else
{
flag = c.Equals(otherElement) == false ? false : flag;
}
}
return flag;
}
}
public class ChildElement : ElementBase<ChildElement>, IEquatable<ChildElement>
{
public ChildElement(XElement xElement)
: base(xElement)
{
ChildElements = new List<ChildElement>();
Name = xElement.Name.ToString();
foreach (XElement e in xElement.Elements())
{
ChildElements.Add(new ChildElement(e));
}
}
public bool Equals(ChildElement other)
{
bool flag = true;
if (this.ChildElements.Count != other.ChildElements.Count())
{
//--Your error handling logic here
flag = false;
}
List<ChildElement> otherList = other.ChildElements;
foreach (ChildElement e in this.ChildElements)
{
ChildElement otherElement = otherList.FirstOrDefault(x => x.Name == e.Name);
if (otherElement == null)
{
//--Your error handling logic here
flag = false;
}
else
{
flag = e.Equals(otherElement) == false ? false : flag;
}
}
return flag;
}
}
If you also want to check for attributes or innerxml, you can do like so.
public List<XAttribute> ElementAttributes = new List<XAttribute>();
foreach (XAttribute attr in xElement.Attributes())
{
ElementAttributes.Add(attr);
}
List<XAttribute> otherAttributes = other.ElementAttributes;
foreach (XAttribute attr in ElementAttributes)
{
XAttribute otherAttribute = otherAttributes.FirstOrDefault(x => x.Name == attr.Name);
if (otherAttribute == null)
{
//--Your error handling logic here
flag = false;
}
else
{
if (otherAttribute.Value != attr.Value)
{
//--Your error handling logic here
flag = false;
}
}
}
Related
How can I check if XElement has Record & INVID attributes ?
My function returns only single XElement. i.e,
<INVENTORY>
<Record>
<INVID>1315</INVID>
<INVNAME>TEST LOCATIONTEST</INVNAME>
<HOSPNAME>TEST LOCATION</HOSPNAME>
<INVTYPE>CLINICAL</INVTYPE>
<INVDT>2013-09-30T09:30:00</INVDT>
<INVDEF>YES</INVDEF>
<INVACT>YES</INVACT>
<UPDDTTM />
<UPDUSR />
<ENBREF>true</ENBREF>
<INVPWD>101315</INVPWD>
</Record>
</INVENTORY>
XElement xInventory = GetDefaultInventory();
bool hasInventory = xInventory.Elements("INVID").Any(); //What to do here ?
if (hasInventory)
{
//TO DO Some action
}
If you want to check that inventory element has Record element with INVID child element, then you can use XPath:
XElement inventory = GetDefaultInventory();
XElement invid = inventory.XPathSelectElement("Record/INVID");
if (invid == null)
// not exist
Or LINQ way:
bool exists = inventory.Elements("Record").Elements("INVID").Any();
How about this :
bool hasInventory = xInventory.Elements("Record").Any() && xInventory.Descendants("INVID").Any();
That will check if xInventory element has direct child Record, and if it has then check if xInventory has child element INVID.
I have an XML file which looks like...
<Modules>
<Module Title='Mod1' Attr2='' Attr3=''>
<Functions>
<Function Title='Fun1' Attr2=''>
<SubFunctions>
<SubFunction Title='SubFun1' Attr2='' />
<SubFunction Title='SubFun2' Attr2='' />
</SubFunctions>
</Function>
<Function Title='Fun2' Attr2='' />
<Function Title='Fun3' Attr2='' />
</Functions>
</Module>
<Module Title='Mod2' Attr2='' Attr3=''>
<Functions>
</Functions>
</Module>
<Module Title='Mod3' Attr2='' Attr3=''>
<Functions>
</Functions>
</Module>
I have been trying to write a generic function based on recursive in nature using LINQ and XML in C# but have not able to get hold on making the function as generic as possible.
What I have currently is my functions loads all the data related "Module", hence the count is 3 as per above XML data. When the user selects 'Mod1' the very first "Module", it should load all respective data which is under this 'Mod1' i.e. Fun1, Fun2, Fun3. Again if the user selects Fun1, it should load all the data under this 'Fun1' i.e. SubFun1, SubFun2 etc.
PLEASE NOTE: I don't want to hardcode any XML tags in my C# application.
Thanks in advance.
If you don't want to hardcode the names of the tags, we should make an assumption about the XML structure. According to your example it seems that there are two kinds of elements:
"Container" element. Modules, Functions, SubFunctions. Which can contains zero or more "Data" element
"Data" element. Module, Function, SubFunction. Which can contain zero or one "Container" element and always has a "Title" attribute which is used for selection.
Note that "Title" attribute name is hardcoded unless you use customized name. Your decisions.
The code can be generic, as we don't care about the names of the Elements, but only their relation.
As you described, user chooses the selected element gradually, following this requirement I would propose the following solution:
MyXmlReader - class that supplies the access to the XML file
DataElement - class that contains data about the queries Element, including its child elements
class MyXmlReader
{
// refference to current list element
XmlNode currentListElement = null;
XmlDocument xml;
// load file, initialize and return data that contains info ablut
// the first level elements names
public DataElement Load(string path)
{
xml = new XmlDocument();
xml.Load(path);
Init();
DataElement result = new DataElement();
result.SubTitles = GetChildTitles();
return result;
}
// Initialize the reader to read from the beggining
public void Init()
{
currentListElement = xml.DocumentElement;
}
// Get next child
public DataElement GetNext(string title)
{
string tempTitle;
foreach (XmlNode child in currentListElement.ChildNodes)
{
DataElement result = null;
if (child.Attributes["Title"].Value == title)
{
// create object that contains the data about the child nodes Titles
result = new DataElement();
result.Title = child.Attributes["Title"].Value;
if (child.FirstChild != null) // means no child nodes
{
currentListElement = child.FirstChild;
// add subelements subtitles
result.SubTitles.AddRange(GetChildTitles());
}
return result;
}
}
return null;
}
public List<string> GetChildTitles()
{
List<string> result = new List<string>();
foreach (XmlNode child in currentListElement.ChildNodes)
{
result.Add(child.Attributes["Title"].Value);
}
return result;
}
}
// add any other data to this class
// that you need about the element you return
class DataElement
{
public List<string> SubTitles = new List<string>();
public string Title { get; set; }
}
Test:
// test
static void Main(string[] args)
{
MyXmlReader reader = new MyXmlReader();
DataElement data = reader.Load("Data.xml");
// Generic test:
// get first module
data = reader.GetNext(data.SubTitles[0]);
// get first function
data = reader.GetNext(data.SubTitles[0]);
// get first sub-function
data = reader.GetNext(data.SubTitles[0]);
// you can write test with hardcode nodes names like this:
reader.Init();
// get first module
data = reader.GetNext("Mod1");
// get first function
data = reader.GetNext("Fun1");
// get first sub-function
data = reader.GetNext("SubFun1");
Console.Read();
}
I store some data as XML and the changes of the user as diff to the original XML, so the data for the user can be patched on runtime.
Example for the original xml (only part of it):
<module id="">
<title id="">
...
</title>
<actions>
...
</actions>
<zones id="" selected="right">
<zone id="" key="right" name="Right" />
</zones>
</module>
Example of the user diff (the user changed the value of selected from right to left):
<xd:xmldiff version="1.0" srcDocHash=""
options="IgnoreChildOrder IgnoreNamespaces IgnorePrefixes IgnoreSrcValidation "
fragments="no"
xmlns:xd="http://schemas.microsoft.com/xmltools/2002/xmldiff">
<xd:node match="1">
<xd:node match="3">
<xd:change match="#selected">left</xd:change>
</xd:node>
</xd:node>
</xd:xmldiff>
The problem is, that the patch looks for the order of the XML nodes. If the order changes than the diff cannot be applied anymore or even worse it will be applied wrongly. So I would prefer patching by XID.
Does anyone know a performant library or algorith for C# for a XyDiff?
We developed now our own solution that works fine.
What did we do:
ensure that every xml node in the original file has an unique id (no
matter on which level)
generate a flat xml patch for user changes that only saves the
changes of each changed node (without the level structure)
if the user changes a value, write a xmlpatch node in the patch xml
with attribute targetid pointing to the id of the original node
if the user changed an attribute, write the attribute with the new
value to the xmlpatch node
if the user changes a value, write the value to the xmlpatch node
The xml patch looks the following:
<patch>
<xmlpatch sortorder="10" visible="true" targetId="{Guid-x}" />
<xmlpatch selected="left" targetId="{Guid-y}" />
<xmlpatch targetId="{Guid-z}">true</xmlpatch>
</patch>
The code to produce the patch xml is pretty easy. We loop throug all xml nodes and for each node through all attributes. If an attribute or value of a node is different to the original, we generate the patch node with the attribute or value. Please note that the code was written in one night ;)
public static XDocument GenerateDiffGram(XDocument allUserDocument, XDocument runtimeDocument)
{
XDocument diffDocument = new XDocument();
XElement root = new XElement("patch");
AddElements(root, runtimeDocument, allUserDocument.Root);
diffDocument.Add(root);
return diffDocument;
}
private static void AddElements(XElement rootPatch, XDocument runtimeDocument, XElement allUserElement)
{
XElement patchElem = null;
if (allUserElement.Attribute("id") != null
&& !string.IsNullOrWhiteSpace(allUserElement.Attribute("id").Value))
{
// find runtime element by id
XElement runtimeElement = (from e in runtimeDocument.Descendants(allUserElement.Name)
where e.Attribute("id") != null
&& e.Attribute("id").Value.Equals(allUserElement.Attribute("id").Value)
select e).FirstOrDefault();
// create new patch node
patchElem = new XElement("xmlpatch");
// check for changed attributes
foreach (var allUserAttribute in allUserElement.Attributes())
{
XAttribute runtimeAttribute = runtimeElement.Attribute(allUserAttribute.Name);
if (!allUserAttribute.Value.Equals(runtimeAttribute.Value))
{
patchElem.SetAttributeValue(allUserAttribute.Name, runtimeAttribute.Value);
}
}
// check for changed value
if (!allUserElement.HasElements
&& !allUserElement.Value.Equals(runtimeElement.Value))
{
patchElem.Value = runtimeElement.Value;
}
}
// loop through all children to find changed values
foreach (var childElement in allUserElement.Elements())
{
AddElements(rootPatch, runtimeDocument, childElement);
}
// add node for changed value
if (patchElem != null
&& (patchElem.HasAttributes
|| !string.IsNullOrEmpty(patchElem.Value)))
{
patchElem.SetAttributeValue("targetId", allUserElement.Attribute("id").Value);
rootPatch.AddFirst(patchElem);
}
}
On runtime we patch the changes saved in the patch xml back. We geht the original node by the targetid and overwrite the attributes and values.
public static XDocument Patch(XDocument runtimeDocument, XDocument userDocument, string modulePath, string userName)
{
XDocument patchDocument = new XDocument(userDocument);
foreach (XElement element in patchDocument.Element("patch").Elements())
{
// get id of the element
string idAttribute = element.Attribute("targetId").Value;
// get element with id from allUserDocument
XElement sharedElement = (from e in runtimeDocument.Descendants()
where e.Attribute("id") != null
&& e.Attribute("id").Value.Equals(idAttribute)
select e).FirstOrDefault();
// element doesn't exist anymore. Maybe the admin has deleted the element
if (sharedElement == null)
{
// delete the element from the user patch
element.Remove();
}
else
{
// set attributes to user values
foreach (XAttribute attribute in element.Attributes())
{
if (!attribute.Name.LocalName.Equals("targetId"))
{
sharedElement.SetAttributeValue(attribute.Name, attribute.Value);
}
}
// set element value
if (!string.IsNullOrEmpty(element.Value))
{
sharedElement.Value = element.Value;
}
}
}
// user patch has changed (nodes deleted by the admin)
if (!patchDocument.ToString().Equals(userDocument.ToString()))
{
// save back the changed user patch
using (PersonalizationProvider provider = new PersonalizationProvider())
{
provider.SaveUserPersonalization(modulePath, userName, patchDocument);
}
}
return runtimeDocument;
}
I was working on a bunch of XMLs that all share an attribute that contains the string "name" in them. The following code selects the attribute with string "name" in it and assign a new value to it.
public void updateXmlFile(string strFileName)
{
try
{
//Load the Document
XmlDocument doc = new XmlDocument();
doc.Load(strFileName);
//Set the changed Value
string newValue = GetUniqueKey();
//Select all nodes in the XML then choose from them
//the first node that contain string "name" in it
XmlNodeList list = doc.SelectNodes("//#*");
XmlNode filteredNode = list.Cast<XmlNode>()
.First(item => item.Name.ToLower().Contains("name"));
//Assign the newValue to the value of the node
filteredNode.Value = newValue;
doc.Save(strFileName);
}
catch (XmlException xex) { Console.WriteLine(xex); }
}
Now a new XMLs were added that dosen't have the string "name" in them, so instead of modifying the attribute with string "name" in it I decided to simply modify the last attribute no matter what it was (not the first)
Can anybody tell me how to do that?
EDIT
Here is an example of my XML
<?xml version="1.0" encoding="utf-8"?>
<CO_CallSignLists Version="24" ModDttm="2010-09-13T06:45:38.873" ModUser="EUADEV\SARE100" ModuleOwner="EUADEVS06\SS2008" CreateDttm="2009-11-05T10:19:31.583" CreateUser="EUADEV\A003893">
<CoCallSignLists DataclassId="E3FC5E2D-FE84-492D-AD94-3ACCED870714" EntityId="E3FC5E2D-FE84-492D-AD94-3ACCED870714" MissionID="4CF71AB2-0D92-DE11-B5D1-000C46F3773D" BroadcastType="S" DeputyInSpecialList="1" SunSpots="1537634cb70c6d80">
<CoCallSigns EntityId="DEBF1DDB-3C92-DE11-A280-000C46F377C4" CmdID="C45F3EF1-1292-DE11-B5D1-000C46F3773D" ModuleID="6CB497F3-AD63-43F1-ACAE-2C5C3B1D7F61" ListType="HS" Name="Reda Sabassi" Broadcast="INTO" PhysicalAddress="37" IsGS="1" HCId="0" CommonGeoPos="1" GeoLat="0.0000000" GeoLong="0.0000000">
<CoRadios EntityId="E1BF1DDB-3C92-DE11-A280-000C46F377C4" RadioType="HF" />
</CoCallSigns>
</CoCallSignLists>
</CO_CallSignLists>
#Alex: You notice that the "SunSpots" attribute (last attribute in the first child element) is successfully changed. But now when I wanna load the XML back into the DB it gives me an error
Here is the modified code
public void updateXmlFile(string strFileName)
{
try
{
XDocument doc = XDocument.Load(strFileName);
XAttribute l_attr_1 = (doc.Elements().First().Elements().First().Attributes().Last());
l_attr_1.Value = GetUniqueKey();
Console.WriteLine("Name: {0} Value:{1}", l_attr_1.Name, l_attr_1.Value);
doc.Save(strFileName);
}
catch (XmlException xex) { Console.WriteLine(xex); }
}
I was thinking of making an if statment which checks if the XML has an attribute that contains string "name" in it (since most of my XMLs has an attribute that contains name in them) if it does then change the attribute's value if not look for the last attribute and change it.. not the best solution but just throwing it out there
Then definitely use Linq to XML.
Example:
using System.Xml.Linq;
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Commands Version=""439"" CreateUser=""Reda"">
<CmCommands DataclassId=""57067ca8-ef96-4d2e-a085-6bd7e8b24126"" OrderName = ""Tea"" Remark=""Black"">
<CmExecutions EntityId=""A9A5B0F2-6AB4-4619-9106-B0F85F86EE01"" Lock=""n"" />
</CmCommands>
</Commands>";
XDocument x = XDocument.Parse(xml);
Debug.Print(x.Elements().First().Elements().First().Attributes().Last().Value);
// Commands ^ CmCommands ^ Remark ^
That is, word for word, the last attribute of the first child of the first element.
You can also query for element/attribute names, like:
Debug.Print(x.Descendants(XName.Get("CmCommands", "")).First().Attribute(XName.Get("Remark", "")).Value);
And of course you can use all of the Linq goodness like Where, Select, Any, All etc.
Note: replace XDocument.Parse with XDocument.Load if appropriate etc.
I've not tested this but you should be able to do all of this in the XPath expression. Something like this:
//#*[contains(node-name(.), 'name')][last()]
This will return only the last attribute with the string name anywhere in its name.
If you only want the last attribute, irrespective of it's name, use this:
//#*[last()]
Look at class XmlAttributeCollection. You can get this collection by reading property Attributes of XmlNode. Just get the last by index.
Instead of .First(), use an extension method like this:
public static T LastOrDefault<T>(this IEnumerable<T> list)
{
T val = null;
foreach(T item in list)
{
val = item;
}
return val;
}
I have the following XML Document (that can be redesigned if necessary) that stores records and errors.
<MYROOT>
<RECORDS>
<RECORD>
<DATETIME>11/03/2010 14:12:41</DATETIME>
<DOCUMENTID>1</DOCUMENTID>
</RECORD>
<RECORD>
<DATETIME>11/03/2010 14:12:44</DATETIME>
<DOCUMENTID>2</DOCUMENTID>
</RECORD>
<RECORD>
<DATETIME>11/03/2010 14:12:45</DATETIME>
<DOCUMENTID>3</DOCUMENTID>
</RECORD>
</RECORDS>
<ERRORS>
<ERROR TYPE="ERR">
<DATETIME>11/03/2010 14:12:41</DATETIME>
<DETAIL>There has been a error on page 1</DETAIL>
</ERROR>
<ERROR TYPE="ERR">
<DATETIME>11/03/2010 14:13:03</DATETIME>
<DETAIL>There has been a error on page 101</DETAIL>
</ERROR>
<ERROR TYPE="SEQ">
<DATETIME>11/03/2010 14:13:03</DATETIME>
<DETAIL>Sequence Error, expected Sequence No. 101 Read 1</DETAIL>
</ERROR>
</ERRORS>
</MYROOT>
I want to output the records and errors but obviously have to sort them by date so they appear in order.
How can I sort them by date, get a collection of XElements and then just do a foreach loop over them?
XDocument xml = System.Xml.Linq.XDocument.Parse(YOUR_XML);
IEnumerable<XElement> records = xml.Root.Element("RECORDS").Elements();
IEnumerable<XElement> errors = xml.Root.Element("ERRORS").Elements();
IEnumerable<XElement> elements = from el in records.Concat(errors)
orderby DateTime.Parse(el.Element("DATETIME").Value)
select el;
foreach (XElement el in elements)
{
// do something.
}
var elements = doc.Descendants("RECORD").Concat(doc.Descendants("ERROR")).
OrderBy(x => DateTime.Parse(x.Element("DATETIME").Value));
foreach (XElement element in elements)
{
// do something interesting with element
}
The IEnumerable is not very flexible, the best option may be to remove the elements from the enumerable, sort them and re-insert them, maintaining the correct order (relative to previous neighbors). It's a little more complicated if a sub element is the sort key
This will remove the named elements from the IEnumerable, sort them by a sub-element (may or may not be what you need) and re-insert them in the right place.
private void SortIdNodes(XElement parent, String elementName, String sortElementname)
{
XNode prevElem = null;
XNode nextElem = null;
// Initial node count, to verify sets are equal
int initialElementsCount = parent.Descendants().Count();
List<XElement> unOrdered = parent.Descendants(elementName).ToList<XElement>();
if (unOrdered.Count() < 2){
return; // No sorting needed
}
// Make note of the neighbors
prevElem = unOrdered[0].PreviousNode;
nextElem = unOrdered.Last().NextNode;
// Remove set from parent
unOrdered.ForEach(el =>
{
el.Remove();
});
// Order the set, language (IEnumerable) semantics prevents us from changing order in place
List <XElement> ordered = unOrdered.OrderBy(x => x.Descendants(sortElementname).FirstOrDefault().Value).ToList<XElement>();
// Add to parent in correct order
if (prevElem != null) // If there's a first neighbor
{
ordered.ForEach(el =>
{
prevElem.AddAfterSelf(el);
prevElem = el;
});
}
else if (nextElem != null) // If there's only an end neighbor
{
ordered.Reverse();
ordered.ForEach(el =>
{
nextElem.AddBeforeSelf(el);
nextElem = el;
});
}
else // we're the only children of the parent, just add
{
ordered.ForEach(el =>
{
parent.Add(el); // add in order
});
}
int finalElementCount = parent.Descendants().Count();
if (initialElementsCount != finalElementCount)
{
throw new Exception("Error with element sorting, output collection not the same size as the input set.");
}
}