Sort Nodes in XmlDocument by Attribute in C++, XPath, Windows Forms
I want to sort a XML-Document by attributes of an Child-Node. I am working with Windows Forms in Visual C++. I tried to use the solution proposed here on Stackoverflow. But somehow it won't work.
My unsorted XML-Doc looks like the following:
<Message-List>
<Message sendTime="0"></Message>
<Message sendTime="20"></Message>
<Message sendTime="5"></Message>
</Message-List>
My sorted XML-Doc should look like this:
<Message-List>
<Message sendTime="0"></Message>
<Message sendTime="5"></Message>
<Message sendTime="20"></Message>
</Message-List>
I tried following Code, but the checkme-String returned is 0205. So the开发者_StackOverflow中文版 sorting doesn't even work.
System::Xml::XmlDocument^ sourceXmlDoc = gcnew XmlDocument;
sourceXmlDoc->LoadXml("<Message-List><Message sendTime=\"0\"></Message><Message sendTime=\"20\"></Message><Message sendTime=\"5\"></Message></Message-List>");
System::Xml::XPath::XPathNavigator^ navigator = sourceXmlDoc->CreateNavigator();
System::Xml::XPath::XPathExpression^ selectExpression = navigator->Compile("Message-List/Message");
System::Xml::XPath::XPathExpression^ sortExpr = navigator->Compile("@sendTime");
selectExpression->AddSort(sortExpr, XmlSortOrder::Ascending, XmlCaseOrder::None, "", XmlDataType::Text);
System::Xml::XPath::XPathNodeIterator^ nodeIterator = navigator->Select(selectExpression);
String^ checkMe;
while (nodeIterator->MoveNext())
{
if (nodeIterator->Current->MoveToFirstAttribute())
{
checkMe = checkMe + nodeIterator->Current->Value;
}
}
Furthermore, I am stuck on how to proceed in the while-loop. How can I save the resorted xmlDoc as XmlDocument?
I might be completely wrong since .NET is not my thing, but isn't your xpath in navigator->Compile wrong and your XML data type in selectExpression->AddSort wrong
System::Xml::XPath::XPathExpression^ selectExpression = navigator->Compile("Message-List/Message");
selectExpression->AddSort(sortExpr, XmlSortOrder::Ascending, XmlCaseOrder::None, "", XmlDataType::Number);
Finally, I managed to sort the XML-Message.
@John: yes you were right, I missed to change the XmlDataType. Thanks for the hint.
The following Solution makes a Copy of a XmlDocument and sorts it by the numerical Attribute "sendTime".
// Create Source-Document for Testing
System::Xml::XmlDocument^ sourceXmlDoc = gcnew XmlDocument;
sourceXmlDoc->LoadXml("<Message-List><Message sendTime=\"0\"></Message><Message sendTime=\"20\"></Message><Message sendTime=\"5\"></Message></Message-List>");
// Create a copy of the input XmlDocument
System::Xml::XmlDocument^ xmlDocCopy = gcnew XmlDocument;
xmlDocCopy = safe_cast<XmlDocument^>(sourceXmlDoc->Clone());
// Only needs to be resorted if there are Messages to be sorted
if ( xmlDocCopy->HasChildNodes )
{
// Remove the unsorted Children
xmlDocCopy->FirstChild->RemoveAll();
// Create a sorted Navigator
System::Xml::XPath::XPathNavigator^ navigator = sourceXmlDoc->CreateNavigator();
System::Xml::XPath::XPathExpression^ selectExpression = navigator->Compile("Message-List/Message");
System::Xml::XPath::XPathExpression^ sortExpr = navigator->Compile("@sendTime");
selectExpression->AddSort(sortExpr, XmlSortOrder::Ascending, XmlCaseOrder::None, "", XmlDataType::Number);
System::Xml::XPath::XPathNodeIterator^ nodeIterator = navigator->Select(selectExpression);
String^ checkMe;
String^ test = nodeIterator->Current->OuterXml;
while (nodeIterator->MoveNext())
{
XmlTextReader^ xmlChildReader = gcnew XmlTextReader(gcnew StringReader(nodeIterator->Current->OuterXml));
XmlNode^ newNode = xmlDocCopy->ReadNode(xmlChildReader);
xmlDocCopy->FirstChild->AppendChild(newNode);
}
}
I just saw this solution, but in .NET 4.0 you could use linq instead. I won't port it to C++, but you'll get the idea! It is a static function that sorts by attribute and the call to this method that replaces the old node with the sorted node.
public static XmlElement OrderChildrenByAttribute(XmlElement originalElement, string attributeName)
{
// Sorting products
var enumList = originalElement.ChildNodes.Cast<XmlElement>();
var sortedList = enumList.OrderBy(p => p.GetAttribute(attributeName).Trim().ToLower());
XmlElement result = originalElement.OwnerDocument.CreateElement(originalElement.Name, originalElement.NamespaceURI);
foreach (var childElem in sortedList)
result.AppendChild(childElem);
return result;
}
And for the call, I use this:
// Sort by "name" attribute
XmlElement elem_params_Products_sorted = XmlTools.OrderChildrenByAttribute(elem_params_Products, "name");
elem_params_Products.ParentNode.ReplaceChild(elem_params_Products_sorted, elem_params_Products);
精彩评论