开发者

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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜