Daniel Leykauf Ответов: 2

Сортировка XML-элементов по их логическому вхождению


Привет,

Я пытаюсь отсортировать XML (дочерние) элементы по их логическому вхождению. Предположим, что в файле есть следующие 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>


В результате Я хотел бы иметь уникальный отсортированный список имен детей: "строка1, строка2, строка3, line4".

"lineXYZ" предназначены только для иллюстраций; означает: названия элементов могут быть разными. Поэтому алфавитная сортировка не может быть применена.
Во 2-й строке "line2 "является новым, но расположен перед" line3 "- по сравнению с предыдущей строкой - и поэтому должен быть ранжирован перед"line3".
В 3-й строке "line4" не существует в предыдущих записях и должен быть добавлен в конце.

Есть ли у кого-нибудь идеи, как этого можно достичь в C#?
Большое спасибо!

Что я уже пробовал:

К сожалению, ни codeproject, ни google до сих пор не предоставили никакого решения.

Patrice T

Покажите результат, который вы хотите получить для этого ввода.

Daniel Leykauf

Как уже упоминалось после xml-блока, я хотел бы иметь сортированные дочерние имена в виде списка unqiue: "line1, line2, line3, line4".

0x01AA

Хммм, XML следует использовать "обычно" без логической сортировки. Но на случай, если вам захочется их отсортировать, я предлагаю linq запросить его.

Daniel Leykauf

Спасибо за ваш ответ! Причина моего вопроса заключается в том, что я хочу визуализировать дочерние узлы в сетке (имена элементов имеют заголовок, строки перечислены с их значениями ниже). Имена заголовков должны отображаться в порядке следования дочерних элементов в зависимости от их вхождения (в родительском элементе). Но не все XML-файлы ссылаются на xsd-файл (где вы можете получить правильную позицию) или являются ложными и могут отсутствовать в каждой строке. Поэтому я должен разобрать каждый узел (в приведенном выше примере элемент "строка") и их дочерние элементы и построить отсортированный список.
Поэтому Linq to query не является альтернативой, так как я не знаю структуры XML-файла и не могу быть именами элементов в не алфавитном порядке, где только позиция описывает правильный порядок.

0x01AA

Добро пожаловать. Теперь я знаю только, как справиться с этим в стиле borland с помощью xslt, который обычно описывает структуру преобразования XML в "плоскую" таблицу, которую вы затем можете сортировать по своему усмотрению. Но да, я признаю, что это вам не поможет, извините :(

Daniel Leykauf

Еще раз спасибо (тоже за потраченное время)!

2 Ответов

Рейтинг:
7

Daniel Leykauf

Я построил свое собственное решение и хочу поделиться им с другими, так как никто не смог его предоставить.

1. я должен разобрать XML по каждому элементу "строки", чтобы получить дочерние узлы:

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. Метод "слияние" выполняет свою работу - он проверяет, находится ли каждый элемент 2-го списка уже в 1-м списке; если нет: он пытается найти одно из следующих значений 2-го списка в 1-м списке; из существующего значения может быть найдено, он вставляет значение 2 - го списка в индекс 1-го, иначе он добавляет его в конец 1-го списка.:
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


Результат находится ли ожидаемый список в правильном порядке:
строка1
строка2
строка3
line4


Рейтинг:
2

RickZeeland

Не проверено, но что-то вроде этого:

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


Daniel Leykauf

К сожалению, это не проблема и не вопрос.
Разбор узлов-самая простая часть. Я хочу получить отсортированные имена детей в соответствии с их появлением в строке.
Поэтому решение должно получить всех детей в строке и вставить новые имена в соответствии с их логическим положением. Для существующих имен индекс должен быть смещен в случае, если новый индекс не равен существующему.

RickZeeland

листчайлдс.Сортировать();

Daniel Leykauf

Привет Рик,
Пожалуйста, взгляните на приведенный выше пример XML и ожидаемый результат. Сортировка строк не может быть использована, так как мне нужно, чтобы она была упорядочена по индексу имени (вместо имени). Спасибо!

RickZeeland

Затем вы можете использовать словарь< int, string> или еще лучше SortedDictionary, смотрите примеры здесь: https://www.dotnetperls.com/dictionary и https://www.dotnetperls.com/sorteddictionary

Daniel Leykauf

К тому же это не решает моей проблемы. Как вы можете видеть в Примере файла, в каждой строке есть разные дети.
Но в целом есть логическая структура, которую я хочу построить.
Например: во 2-й строке строка 2 является новой по сравнению с первой строкой и указана перед строкой 3 (из ее индекса), потому что строка 3 находится во 2-й строке после строки 2.
Поэтому индекс на одно значение должен быть сдвинут. Сортировка по имени не может быть использована, так как имена элементов могут иметь разные имена. Можно использовать только значения из предыдущей строки в текущую, и каждый индекс должен быть изменен в соответствии с индексом предыдущей строки.

Еще один простой образец на линию:

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

Результат должен быть таким: "abc, def, ghi, jkl"

Пожалуйста, не обращайте внимания на названия образцов; они также могут быть: "xyz, abc, zuv, ooh" или что-то еще. Поэтому алфавитная сортировка не может быть использована! Сортировка должна быть применена по индексу (с учетом следующего и предыдущего значения), сопоставленному со значением индекса в предыдущей строке.
Если есть значения, которые не существовали раньше, они должны быть добавлены в конце.
Но если одна из следующих строк уже содержит это значение, индекс должен быть установлен в правильное положение.