XSLT: Как исключить пустые элементы из моего результата? - PullRequest
8 голосов
/ 25 апреля 2010

У меня довольно сложный лист xslt, преобразующий один формат xml в другой с использованием шаблонов. Однако в полученном xml мне нужно исключить все пустые элементы. Как это сделать?

Вот так выглядит базовый xslt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:far="http://www.itella.com/fargo/fargogate/" xmlns:a="http://tempuri.org/XMLSchema.xsd" xmlns:p="http://tempuri.org/XMLSchema.xsd">
    <xsl:import href="TransportCDMtoFDM_V0.6.xsl"/>
    <xsl:import href="ConsignmentCDMtoFDM_V0.6.xsl"/>
    <xsl:template match="/">
        <InboundFargoMessage>
            <EdiSender>
                <xsl:value-of select="TransportInformationMessage/SenderId"/>
            </EdiSender>
            <EdiReceiver>
                <xsl:value-of select="TransportInformationMessage/RecipientId"/>
            </EdiReceiver>
            <EdiSource>
                <xsl:value-of select="TransportInformationMessage/Waybill/Parties/Consignor/Id"/>
            </EdiSource>
            <EdiDestination>FARGO</EdiDestination>
            <Transportations>
                <xsl:for-each select="TransportInformationMessage/TransportUnits/TransportUnit">
                    <xsl:call-template name="transport"/>
                </xsl:for-each>
                <xsl:for-each select="TransportInformationMessage/Waybill/TransportUnits/TransportUnit">
                    <xsl:call-template name="transport"/>
                </xsl:for-each>
                <xsl:for-each select="TransportInformationMessage/Waybill">
                    <EdiImportTransportationDTO>
                        <Consignments>
                            <xsl:for-each select="Shipments/Shipment">
                                <xsl:call-template name="consignment"/>
                            </xsl:for-each>
                        </Consignments>
                        <EdiTerminalDepartureTime>
                            <xsl:value-of select="DatesAndTimes/EstimatedDepartureDateTime"/>
                            <xsl:value-of select="DatesAndTimes/DepartureDateTime"/>
                        </EdiTerminalDepartureTime>
                        <EdiAgentTerminalArrivalDate>
                            <xsl:value-of select="DatesAndTimes/EstimatedArrivalDateTime"/>
                            <xsl:value-of select="DatesAndTimes/ArrivalDateTime"/>
                        </EdiAgentTerminalArrivalDate>
                        <EdiActivevehicle>
                            <xsl:value-of select="Vehicle/TransportShiftNumber"/>
                        </EdiActivevehicle>
                        <EdiConveyerZipCodeTown><xsl:text> </xsl:text></EdiConveyerZipCodeTown>
                    </EdiImportTransportationDTO>
                </xsl:for-each>
            </Transportations>
        </InboundFargoMessage>
    </xsl:template>
</xsl:stylesheet>

Что нужно добавить, чтобы пустые элементы были пропущены?

Например, фрагмент из полученного XML:

<?xml version="1.0" encoding="UTF-8"?>
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd"
        xmlns:far="http://www.itella.com/fargo/fargogate/"
        xmlns:a="http://tempuri.org/XMLSchema.xsd">
    <EdiSender>XXXX</EdiSender>
    <EdiReceiver>YYYY</EdiReceiver>
    <EdiSource>TR/BAL/IST</EdiSource>
    <EdiDestination>FARGO</EdiDestination>
    <Transportations>
        <EdiImportTransportationDTO>
            <Consignments>
                <EdiImportConsignmentDTO>
                    <ConsignmentLines>
                        <EdiImportConsignmentLineDTO>
                            <DangerousGoodsItems>
                                <EdiImportDangerGoodsItemDTO>
                                    <EdiKolliTypeOuter/>
                                    <EdiKolliTypeInner/>
                                    <EdiTechnicalDescription/>
                                    <EdiUNno/>
                                    <EdiClass/>
                                    <EdiDangerFactor/>
                                    <EdiEmergencyTemperature/>
                                </EdiImportDangerGoodsItemDTO>
                            </DangerousGoodsItems>
                            <BarCodes>
                                <EdiImportConsignmentLineBarcodeDTO/>
                            </BarCodes>
                            <EdiNumberOfPieces>00000002</EdiNumberOfPieces>
                            <EdiGrossWeight>0.000</EdiGrossWeight>
                            <EdiHeight/>
                            <EdiWidth/>
                            <EdiLength/>
                            <EdiGoodsDescription/>
                            <EdiMarkingAndNumber/>
                            <EdiKolliType>road</EdiKolliType>
                            <EdiCbm/>
                            <EdiLdm/>
                        </EdiImportConsignmentLineDTO>

Это действительно должно быть:

<?xml version="1.0" encoding="UTF-8"?>
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd"
        xmlns:far="http://www.itella.com/fargo/fargogate/"
        xmlns:a="http://tempuri.org/XMLSchema.xsd">
    <EdiSender>XXXX</EdiSender>
    <EdiReceiver>YYYY</EdiReceiver>
    <EdiSource>TR/BAL/IST</EdiSource>
    <EdiDestination>FARGO</EdiDestination>
    <Transportations>
        <EdiImportTransportationDTO>
            <Consignments>
                <EdiImportConsignmentDTO>
                    <ConsignmentLines>
                        <EdiImportConsignmentLineDTO>
                            <DangerousGoodsItems/>
                            <BarCodes/>
                            <EdiNumberOfPieces>00000002</EdiNumberOfPieces>
                            <EdiGrossWeight>0.000</EdiGrossWeight>
                            <EdiKolliType>road</EdiKolliType>
                        </EdiImportConsignmentLineDTO>

Другими словами: пустые элементы должны быть пропущены.

Ответы [ 4 ]

11 голосов
/ 25 апреля 2010

Предоставленный (частичный) XSLT-код хорошо иллюстрирует антишаблон XSLT. Старайтесь почти всегда избегать использования <xsl:for-each>.

Ниже приведен образец XML-документа и преобразование, которое копирует все узлы за исключением «пустых» элементов. Здесь под «пустым» мы подразумеваем либо бездетный, либо с одним дочерним узлом, доступным только для пробелов.

XML-документ :

<a>
 <b>
   <c>  </c>
   <d/>
   <e>1</e>
 </b>
</a>

Transformation

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "*[not(node())]
  |
   *[not(node()[2])
   and
     node()/self::text()
   and
     not(normalize-space())
     ]
  "/>
</xsl:stylesheet>

Результат:

<a>
   <b>
      <e>1</e>
   </b>
</a>

Примечание :

  1. Использование правила идентификации .

  2. Как мы переопределяем правило Identity шаблоном, который соответствует только «пустым» элементам. Поскольку этот шаблон ничего не делает (вообще не имеет тела), он не копирует («удаляет») «пустые» элементы.

Использование и переопределение правила идентификации является наиболее важным шаблоном проектирования XSLT.

1 голос
/ 25 апреля 2010

Это, наверное, самый простой способ:

<xsl:for-each select="Nodes/Node[text() != '']">

</xsl:for-each>

Если у вас есть контроль над генерацией XML, не добавляйте корневой узел, если нет дочерних элементов. Независимо от того, какой путь вы выберете, XSL довольно многословен.

0 голосов
/ 28 ноября 2017

Я начал с решения Димитра выше (спасибо!), Но у меня все еще были выходные или нулевые элементы с нулевыми потомками, например:

                 <a>
                    <b>
                       <c/>
                       <d/>           
                    </b>             
                 </a>

Это похоже на работу ... все еще тестирую.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:date="http://exslt.org/dates-and-times" 
  xmlns:exsl="http://exslt.org/common" 
  xmlns:func="http://exslt.org/common"  
  xmlns:random="http://exslt.org/random"
  xmlns:regexp="http://exslt.org/regular-expressions" 
  xmlns:set="http://exslt.org/sets" 
  xmlns:str="http://exslt.org/strings" 
  version="1.0" 
  extension-element-prefixes="date exsl func random regexp set str">

  <xsl:output 
    method="xml" 
    encoding="utf-8" 
    omit-xml-declaration="no" 
    indent="yes"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match=
    "*[not(node())]
    |
    *[not(string())]
    "/>
</xsl:stylesheet>
0 голосов
/ 26 апреля 2010

В некоторых хитрых случаях ответ Димитра (что, безусловно, является правильным подходом) может вести себя неожиданно. Например, если вы реорганизовали свой XSLT для использования шаблона идентификации (что вам следует), и вы создали такой шаблон:

<xsl:template match="Vehicle/TransportShiftNumber[. != '123']">
   <EdiActivevehicle>
      <xsl:value-of select="."/>
   </EdiActivevehicle> 
</xsl:template>

преобразование может по-прежнему создавать пустые EdiActivevehicle элементы, если TransportShiftNumber пусто.

Обычно, если несколько шаблонов соответствуют узлу, будет выбран более конкретный. «Более конкретный» обычно означает, что шаблоны с предикатом превзойдут шаблоны, которые этого не делают. (Фактические правила разрешения конфликтов более сложны; см. Раздел 5.5 рекомендации XSLT.) В этом случае и вышеприведенный шаблон, и шаблон с пустым элементом используют предикаты, и, следовательно, оба имеют одинаковый приоритет.

Таким образом, процессор XSLT сделает одну из двух вещей: он сообщит об ошибке (это разрешено, хотя я никогда не видел недружелюбный процессор XSLT), или он выберет шаблон, который появляется latest в таблице стилей.

Есть два способа это исправить. Либо поместите шаблон фильтрации пустых элементов внизу таблицы стилей, либо явным образом назначьте ему приоритет, превышающий 0,5 (что является значением по умолчанию для большинства шаблонов, имеющих предикаты):

Вероятно, я бы сделал последнее, потому что я обычно структурирую таблицы стилей, ожидая, что порядок шаблонов не будет значительным, и я не хочу никаких неприятных сюрпризов, если начну что-то менять. Но я бы обязательно добавил туда комментарий, объясняющий себя: я никогда не видел, чтобы кто-то на самом деле использовал явный приоритет для шаблона.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...