Атрибутами считаются пропавшими без вести после сортировки элементов


Мне нужно создать таблицу стилей XSLT 2.0, которая обрабатывает полную периодическую таблицу , сортирует атомы (только их имена) в группы на основе их состояния (т. е. STATE атрибут элемента ATOM) в текстовый файл.

Я что-то сделал, но есть элементы <ATOM> без атрибутов STATE. Эти элементы также должны присутствовать в итоговом документе.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:template match="PERIODIC_TABLE">
      <xsl:for-each-group select="ATOM" group-by="@STATE" >
        <xsl:text> **    </xsl:text>
        <xsl:value-of select="current-grouping-key()"/>
        <xsl:text>
</xsl:text>
        <xsl:for-each select="current-group()">
          <xsl:sort select="NAME"/>
          <xsl:value-of select="NAME"/>
          <xsl:text>
</xsl:text>
        </xsl:for-each>
        <xsl:text>
</xsl:text>
      </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>
2 2

2 ответа:

Я лишь слегка коснулся вашего кода, сделав следующее изменение :

Заменить:

group-by="@STATE"

С:

group-by="(@STATE, 'UNKNOWN')[1]"

Преобразование теперь :

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
    <xsl:template match="PERIODIC_TABLE">
      <xsl:for-each-group select="ATOM"
        group-by="(@STATE, 'UNKNOWN')[1]" >
        <xsl:text> **    </xsl:text>
        <xsl:value-of select="current-grouping-key()"/>
        <xsl:text>
</xsl:text>
        <xsl:for-each select="current-group()">
          <xsl:sort select="NAME"/>
          <xsl:value-of select="NAME"/>
          <xsl:text>
</xsl:text>
        </xsl:for-each>
        <xsl:text>
</xsl:text>
      </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

И желаемый, правильный результат получается :

 **    UNKNOWN
Actinium
Aluminum
Americium
Antimony
Arsenic
Astatine
Barium
Berkelium
Beryllium
Bohrium
Boron
Bromine
Cadmium
Calcium
Californium
Cerium
Cesium
Chlorine
Chromium
Cobalt
Copper
Curium
Dubnium
Dysprosium
Einsteinium
Erbium
Europium
Fermium
Fluorine
Francium
Gadolinium
Gallium
Germanium
Hafnium
Hassium
Holmium
Indium
Iodine
Iridium
Iron
Krypton
Lanthanum
Lawrencium
Lead
Lithium
Lutetium
Magnesium
Manganese
Meitnerium
Mendelevium
Mercury
Molybdenum
Neodymium
Neon
Neptunium
Nickel
Niobium
Nitrogen
Nobelium
Osmium
Oxygen
Palladium
Phosphorus
Platinum
Plutonium
Polonium
Potassium
Praseodymium
Promethium
Protactinium
Radium
Radon
Rhenium
Rhodium
Rubidium
Ruthenium
Rutherfordium
Samarium
Scandium
Seaborgium
Selenium
Silicon
Silver
Sodium
Strontium
Sulfur
Tantalum
Technetium
Tellurium
Terbium
Thallium
Thorium
Thulium
Tin
Titanium
Tungsten
Uranium
Vanadium
Ytterbium
Yttrium
Zinc
Zirconium
ununbium
ununnilium
unununium

 **    GAS
Argon
Helium
Hydrogen
Xenon

 **    SOLID
Bismuth
Carbon
Gold

Вы можете просто сделать xsl:for-each для всех атомов, которые не имеют атрибута состояния. Вы могли бы разграничить это с чем-то вроде "другого".

Эта измененная версия вашей таблицы стилей:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="PERIODIC_TABLE">
    <xsl:for-each-group select="ATOM" group-by="@STATE">
      <xsl:text> **    </xsl:text>
      <xsl:value-of select="current-grouping-key()"/>
      <xsl:text>&#xA;</xsl:text>
      <xsl:for-each select="current-group()">
        <xsl:sort select="NAME" case-order="#default"/>
        <xsl:value-of select="NAME"/>
        <xsl:text>&#xA;</xsl:text>
      </xsl:for-each>
      <xsl:text>&#xA;</xsl:text>
    </xsl:for-each-group>
    <xsl:if test="ATOM[not(@STATE)]">
      <xsl:text> **    OTHER&#xA;</xsl:text>
      <xsl:for-each select="ATOM[not(@STATE)]">
        <xsl:sort select="NAME" case-order="#default"/>
        <xsl:value-of select="NAME"/>
        <xsl:text>&#xA;</xsl:text>      
      </xsl:for-each>      
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Выдает следующий результат (во всей красе):

 **    GAS
Argon
Helium
Hydrogen
Xenon

 **    SOLID
Bismuth
Carbon
Gold

 **    OTHER
Actinium
Aluminum
Americium
Antimony
Arsenic
Astatine
Barium
Berkelium
Beryllium
Bohrium
Boron
Bromine
Cadmium
Calcium
Californium
Cerium
Cesium
Chlorine
Chromium
Cobalt
Copper
Curium
Dubnium
Dysprosium
Einsteinium
Erbium
Europium
Fermium
Fluorine
Francium
Gadolinium
Gallium
Germanium
Hafnium
Hassium
Holmium
Indium
Iodine
Iridium
Iron
Krypton
Lanthanum
Lawrencium
Lead
Lithium
Lutetium
Magnesium
Manganese
Meitnerium
Mendelevium
Mercury
Molybdenum
Neodymium
Neon
Neptunium
Nickel
Niobium
Nitrogen
Nobelium
Osmium
Oxygen
Palladium
Phosphorus
Platinum
Plutonium
Polonium
Potassium
Praseodymium
Promethium
Protactinium
Radium
Radon
Rhenium
Rhodium
Rubidium
Ruthenium
Rutherfordium
Samarium
Scandium
Seaborgium
Selenium
Silicon
Silver
Sodium
Strontium
Sulfur
Tantalum
Technetium
Tellurium
Terbium
Thallium
Thorium
Thulium
Tin
Titanium
Tungsten
ununbium
ununnilium
unununium
Uranium
Vanadium
Ytterbium
Yttrium
Zinc
Zirconium
Несколько других вещей, которые я сделал, это добавил атрибуты case-order к элементам xsl:sort, и я использовал ссылку на шестнадцатеричную сущность &#xA; для новых строк.

Еще одна вещь, которую я обычно делаю, но не сделал здесь, - это использование concat() вместо того, чтобы иметь отдельный xsl:text для новых строк / пробелов.

Вместо этого:

<xsl:value-of select="NAME"/>
<xsl:text>&#xA;</xsl:text>

Вы можете сделать это:

<xsl:value-of select="concat(NAME,'&#xA;')"/>