Как отсортировать XML-элементы на месте?

Я пытаюсь контролировать конфигурационные файлы IntelliJ IDEA. Вот небольшой пример:

<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ChangeListManager"> <ignored path="tilde.iws" /> <ignored path=".idea/workspace.xml" /> <ignored path=".idea/dataSources.local.xml" /> <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> <option name="TRACKING_ENABLED" value="true" /> <option name="SHOW_DIALOG" value="false" /> <option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="LAST_RESOLUTION" value="IGNORE" /> </component> <component name="ToolWindowManager"> <frame x="1201" y="380" width="958" height="1179" extended-state="0" /> <editor active="false" /> <layout> <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> <window_info id="Palette	" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> </layout> </component> </project> 

Некоторые элементы, такие как /project/component[@name='ToolWindowManager']/layout/window_info кажутся сохраненными в произвольной последовательности каждый раз, когда среда IDE сохраняет конфигурацию. Все элементы одного типа, похоже, всегда имеют одинаковые атрибуты в одной и той же последовательности. Учитывая, что последовательность элементов не имеет отношения к функционированию IDE, было бы полезно, если элементы сортируются по имени элемента, а затем значения атрибутов, а атрибуты и пробелы остаются на месте.

Основываясь на другом ответе, я понял:

 <stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform"> <output method="xml" indent="yes" encoding="UTF-8"/> <strip-space elements="*"/> <template match="processing-instruction()|@*"> <copy> <apply-templates select="node()|@*"/> </copy> </template> <template match="*"> <copy> <apply-templates select="@*"/> <apply-templates> <sort select="name()"/> <sort select="@*[1]"/> <sort select="@*[2]"/> <sort select="@*[3]"/> <sort select="@*[4]"/> <sort select="@*[5]"/> <sort select="@*[6]"/> </apply-templates> </copy> </template> </stylesheet> 

Это почти все, но с несколькими проблемами:

  • Он не сортирует по каждому значению атрибута (и @* не работает)
  • Он удаляет пространство до конца пустых элементов ( <foo /> становится <foo/> ).
  • Он добавляет новую строку в EOF (что ИМО не является ошибкой, но делает результирующий файл менее похожим на оригинал).

Я не уверен в деталях канонической сортировки xml и, если она соответствует тому, что вы описали, однако я бы рекомендовал использовать xmllint для канонической сортировки xml, прежде чем сохранять файл в исходном элементе управления. Если вы согласны в этом, то ваш контроль версий должен быть очень чистым и полезным. Вы можете переделать материал ниже, чтобы быть в скрипте, и если вы используете git, вы можете настроить githook чтобы githook сценарий для вас.

 $ xmllint --c14n originalConfig.xml > sortedConfig.xml $ mv sortedConfig.xml originalConfig.xml 

Вышеупомянутое должно работать для вас, если вы используете linux или mac. Возможно, вам придется установить что-то вроде cygwin, если вы используете окна.