Използване на булеви стойности за форматиране на низове в XSLT

Имам няколко записа с информация, които искам да разделя със запетая. Всеки запис обаче може да е празен и ако първият запис е празен, запетая не трябва да се появява. Например:

ако имаме четири XSLT параметъра: име, телефонен номер, адрес, професия

и ние имаме

  • Име: Джон
  • Телефон: 111-111-1111
  • Адрес: въображаема улица
  • Професия: Пекар

Тогава крайният низ трябва да бъде:

Джон, 111-111-1111, въображаема улица, Бейкър

ако параметрите за име и телефонен номер са празни или нулеви, тогава крайният низ трябва да бъде:

Въображаема улица, Бейкър

ако само телефонният номер е нулев или празен, тогава крайният низ трябва да бъде:

Джон, въображаема улица, Бейкър

На език като C# бих написал кода така:

foreach (EntryObject entry in entryList)
{
    if (firstEntry == true && entry.Type != EntryType.Age && entry.Type != EntryType.Sex)
    {
        finalString += entry.ValueString;
        firstEntry = false;
    }
    else if (firstEntry == false && entry.Type != EntryType.Age && entry.Type != EntryType.Sex)
    { 
        finalString += ", " + entry.ValueString;
    }
}
return finalString;

Чух обаче, че променливите в XSLT са неизменни. Как да подходя към този проблем в XSLT?

Редактиране: xml записът ще изглежда така:

<AddressBook>
    <PersonalInfo>
        <Age>33</Age>
        <Sex>Male</Sex>
        <Name>John</Name>
        <PhoneNumber></PhoneNumber>
        <Address>Imaginary Street</Address>
        <Occupation>Baker</Occupation>
    </PersonalInfo>
</AddressBook>

Имайте предвид, че някои записи може да са празни и ще използвам само име, телефонен номер, адрес и професия. Възрастта и пола трябва да се пренебрегват.


person J.L    schedule 26.11.2012    source източник
comment
Как изглежда вашият XML вход?   -  person Matthew Green    schedule 27.11.2012
comment
Това XSLT 1.0 ли е или XSLT 2.0?   -  person Michael Kay    schedule 27.11.2012
comment
@MatthewGreen Редактирах оригиналната си публикация, за да включа пример.   -  person J.L    schedule 27.11.2012
comment
@MichaelKay Използвам XSLT 1.1. (Нов съм в XSLT, но предполагам това от записа ‹xsl:stylesheet version=1.1 ...›   -  person J.L    schedule 27.11.2012
comment
@J.L, вижте много общо, просто и кратко решение.   -  person Dimitre Novatchev    schedule 27.11.2012
comment
@J.L, няма такова нещо като XSLT 1.1; тя се отнася до работна чернова, която беше изоставена през 2001 г. За съжаление едно издание на книгата ми направи обширна препратка към нея и беше публикувана точно когато беше изоставена, така че читателите на книгата имат навика да използват 1.1, без да осъзнават. Така че стиловият лист може да бъде 1.0 стилов лист или 2.0 стилов лист. Двете версии са МНОГО различни.   -  person Michael Kay    schedule 27.11.2012
comment
@MichaelKay Не съм сигурен къде другаде мога да проверя, за да намеря номера на версията. Извинявам се, ако това звучи много елементарно, аз съм сравнително нов за XSLT.   -  person J.L    schedule 27.11.2012


Отговори (2)


Използвайте условие на XPath, което отговаря само на елементите, които не са празни (string-length(.)>0 или просто string(.)), и след това използвайте функцията position(), за да проверите дали даден елемент е първи или не. Въведете XML:

<root>
  <item>
    <name>John</name>
    <phoneNumber>111-111-1111</phoneNumber>
    <address>Imaginary street</address>
    <occupation>Baker</occupation>
  </item>
  <item>
    <name>Jane</name>
    <address>Another street</address>
    <occupation>Decorator</occupation>
  </item>
  <item>
    <address>Unknown</address>
  </item>
</root>

XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="item">
    <textItem>
      <xsl:for-each select="*[string(.)]">
        <xsl:if test="position()>1">
          <xsl:text>,</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
      </xsl:for-each>
    </textItem>
  </xsl:template>

  <xsl:template match="text()">
  </xsl:template>

  <xsl:template match="root">
    <root>
      <xsl:apply-templates/>
    </root>
  </xsl:template>

</xsl:stylesheet>

където първият шаблон е този, който действително върши работата, <xsl:template match="text()"> премахва текста, съдържащ се в несъвпадащи елементи (по подразбиране XSLT процесорът ще копира такъв текст в изхода) и <xsl:template match="root"> генерира основния елемент на изходния документ.

Резултат:

<root>
  <textItem>John,111-111-1111,Imaginary street,Baker</textItem>
  <textItem>Jane,Another street,Decorator</textItem>
  <textItem>Unknown</textItem>
</root>

Ако се интересувате само от някои от полетата, просто ги изберете с помощта на оператора за обединение (|) - напр. ако искате само телефон, адрес и професия в примера по-горе, променете XSLT да бъде:

  <xsl:template match="item">
    <textItem>
      <xsl:for-each select="(phoneNumber|address|occupation)[string(.)]">
        <xsl:if test="position()>1">
          <xsl:text>,</xsl:text>
        </xsl:if>
        <xsl:value-of select="."/>
      </xsl:for-each>
    </textItem>
  </xsl:template>

  <xsl:template match="text()">
  </xsl:template>

  <xsl:template match="root">
    <root>
      <xsl:apply-templates/>
    </root>
  </xsl:template>

</xsl:stylesheet>
person MiMo    schedule 26.11.2012
comment
Благодаря ви за отговора, но след като прегледах дадения ми xml, разбрах, че има други елементи, които не използвам под този елемент - например под ‹item› ще има възраст, пол, име, телефонен номер, адрес и професия и ще използвам само име, телефонен номер, адрес и професия - така че тази стратегия за проверка на позиция()›1 няма да работи. За съжаление нямам контрол върху дадения xml. - person J.L; 27.11.2012
comment
за какво са ‹xsl:template match=text()› и ‹xsl:template match=root›? - person J.L; 27.11.2012
comment
Също така, мога ли да използвам този код под fo:block? ex) ‹fo:block ...› ‹xsl:template match=item›...... ‹/xsl:template› ‹/fo:block ...› - person J.L; 27.11.2012
comment
Вижте допълнителни обяснения в отговора относно <xsl:template match="text()"> и <xsl:template match="root">. Не разбирам въпроса за <fo:block> - предлагам да създадете нов въпрос за това. - person MiMo; 28.11.2012

Ето общо решение, което приема списъка с имена на елементи в „черен списък“ като параметър:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pIgnoreThese" select="' Age Sex '"/>

 <xsl:template match="PersonalInfo">
  <xsl:apply-templates select=
   "*[normalize-space()
    and
      not(contains($pIgnoreThese, concat(' ', name(), ' ')))
     ]"/>
   <xsl:text>&#xA;</xsl:text>
 </xsl:template>

 <xsl:template match="PersonalInfo/*">
  <xsl:if test="position() >1">, </xsl:if>
  <xsl:value-of select="."/>
 </xsl:template>
</xsl:stylesheet>

Когато тази трансформация се приложи към предоставения XML документ:

<AddressBook>
        <PersonalInfo>
            <Age>33</Age>
            <Sex>Male</Sex>
            <Name>John</Name>
            <PhoneNumber>111-111-1111</PhoneNumber>
            <Address>Imaginary Street</Address>
            <Occupation>Baker</Occupation>
        </PersonalInfo>
        <PersonalInfo>
            <Age>33</Age>
            <Sex>Male</Sex>
            <Name></Name>
            <PhoneNumber></PhoneNumber>
            <Address>Imaginary Street</Address>
            <Occupation>Baker</Occupation>
        </PersonalInfo>
        <PersonalInfo>
            <Age>33</Age>
            <Sex>Male</Sex>
            <Name>John</Name>
            <PhoneNumber></PhoneNumber>
            <Address>Imaginary Street</Address>
            <Occupation>Baker</Occupation>
        </PersonalInfo>
</AddressBook>

желаният правилен резултат се получава:

John, 111-111-1111, Imaginary Street, Baker
Imaginary Street, Baker
John, Imaginary Street, Baker
person Dimitre Novatchev    schedule 27.11.2012