Click here to Skip to main content
15,117,719 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

I am trying to sort XML (child) elements by their logical occurrence. Assume there are following xml elements in a file:

XML
<row id="1">
	<line1>value</line1>
	<line3>value</line3>
</row>
<row id="2">
	<line1>value</line1>
	<line2>value</line2>
	<line3>value</line3>
</row>
<row id="3">
	<line2>value</line2>
	<line4>value</line4>
</row>
<row id="4">
	<line2>value</line2>
	<line3>value</line3>
	<line4>value</line4>
</row>


As result I would like to have an unique sorted list of child names: "line1, line2, line3, line4".

"lineXYZ" are only for illustrations; means: the element names could be different. Therefore an alphabetical sorting can not be applied.
In 2nd row "line2" is new but located before "line3" - compared to previous row - and needs therefore ranked before "line3".
In 3rd row "line4" does not exists in previous entries and has to append at the end.

Has someone any idea how this can be achieved in C#?
Many thanks!

What I have tried:

Unfortuantely codeproject nor google did provide any solution so far.
Posted
Updated 14-Jul-17 4:10am
v3
Comments
Patrice T 18-Jun-17 10:15am
   
Show the result you want for this input.
Daniel Leykauf 18-Jun-17 10:18am
   
As already mentioned after the xml block, I would like to have the sorted child names as unqiue list: "line1, line2, line3, line4".
[no name] 18-Jun-17 11:47am
   
Hmmm, XML should be used "usually" without a Logical sorting. But in case you like to have sorted them I suggest linq to query it.
Daniel Leykauf 18-Jun-17 12:13pm
   
Thanks for your reply! The reason for my question is that I want to visualize the child nodes in a grid (elements names has header, rows listed with their values below). The header names should appear in order of the child elements based on their occurances (in the parent element). But not all XML files are referenced to a xsd file (where you can get the correct position) or are mendatory and might be missing per row. Therefore I have to parse each node (in above sample the "row" element) and their childs and to build a sorted list.
Linq to query is therefore no alternate as I do not know the XML file structure nor could be the element names in an non-alphabetically order where only the position describes the correct order.
[no name] 18-Jun-17 12:31pm
   
You are welcome. Now I know only how to handle this in borland style with an xslt which generally describes the structure how to convert an XML into a "flat" table, which you can then sort according ever you like. But yes, I recognize this will not help you, sorry :(
Daniel Leykauf 18-Jun-17 12:43pm
   
Thanks once again (also for the time you spent for)!

Not tested, but something like this:
C#
var listChilds = new List<string>;

using (XmlReader reader = XmlReader.Create("test.xml"))
{
	while (reader.Read())
	{
		if (reader.IsStartElement())
		{
			if (!listChilds.contains(reader.Name))
				listChilds.Add(reader.Name);
		}
	}
}
   
Comments
Daniel Leykauf 20-Jun-17 13:36pm
   
That is unfortunately not the issue and question.
The node parsing is the easiest part. I want to get the sorted child names according to their occurance per row.
Therefore a solution has to get all childs per row and has to insert new names according to their logical position. For existing names the index has to shifted in case the new index is unequal to the existing one.
RickZeeland 20-Jun-17 13:47pm
   
listChilds.Sort();
Daniel Leykauf 20-Jun-17 13:50pm
   
Hi Rick,
Please have a look at the XML sample above and the expected result. A string sorting cannot be used as I need it ordered by the name index (instead the name). Thanks!
RickZeeland 20-Jun-17 15:48pm
   
Then you can use a Dictionary<int,string> or even better SortedDictionary, see examples here: https://www.dotnetperls.com/dictionary and https://www.dotnetperls.com/sorteddictionary
Daniel Leykauf 20-Jun-17 16:08pm
   
Also that does not solve my problem. As you can see in the sample file there are per row different childs.
But overall is there a logical structure that I want to build.
For example: in 2nd row line2 is new compared to first row and listed before line3 (from its index) because line3 is in 2nd row after line2.
Therefore the index per value has to be shifted. A sorting by name cannot be used as the element names can be have different names. Only the values from previous to current row can be used and each index has to be changed campared to previous row index.

One other simple sample per line:

1: abc, def, jkl
2: def, ghi, jkl

The result should be: "abc, def, ghi, jkl"

Please ignore the sample names; they could also be: "xyz, abc, zuv, ooh" or whatever. Therefore an alphabetical sorting cannot be used! The sorting needs to by applied by the index (considering the next and previous value) campared with the value index in the line before.
If there are values those are not existing before, they have to be added at the end.
But if one of next line contains that value already, the index has to be set the correct position.
I built my own solution and want to share it to others as nobody was able to provide one.

1. I have to parse the XML by each 'row' element to get the child nodes:
VB
Dim lst As List(Of String) = Nothing ' use as list to store results
For Each c As XmlNode In n.ChildNodes ' loop throw each 'row' element
    Dim l As New List(Of String)
    For Each ch As XmlNode In c.ChildNodes ' loop throw each child of 'row'
        If ch.NodeType = XmlNodeType.Element Then l.Add(ch.Name) ' store element in list
    Next
    Merge(lst, l) ' merge both lists
Next

For Each s As String In lst
    Debug.Print(s)
Next


2. The method 'Merge' does the job - it checks if each element of 2nd list is already in 1st list; if not: it tries to find one of the next values of 2nd list in 1st list; of an existing value could be found, it inserts the value of 2nd list at index of 1st otherwise it adds it to end of 1st list:
VB
Sub Merge(ByRef a As List(Of String), b As List(Of String))
    If a Is Nothing OrElse a.Count = 0 Then a = b

    ' check each item of 2nd list if exists in first list
    For i As Integer = 0 To b.Count - 1
        If Not a.Contains(b(i)) Then
            Dim index As Integer = -1

            ' check if one of the next items are already in first list
            For j As Integer = i + 1 To b.Count - 1
                If a.Contains(b(j)) Then
                    ' use index to append new value before existing value
                    index = a.IndexOf(b(j))
                    Exit For
                End If
            Next

            If Not index = -1 Then
                a.Insert(index, b(i)) ' append new value at index
            Else
                a.Add(b(i)) ' add value at end of list
            End If
        Else
            ' value exists already in first list; do nothing
        End If
    Next
End Sub


The result is the expected list in correct order:
line1
line2
line3
line4
   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900