Если в узле несколько значений, объедините их в одну строку - PullRequest
2 голосов
/ 23 декабря 2011

Я новичок в программировании xslt и мне нужно решение проблемы.Я хочу преобразовать XML-файл в текстовый файл CSV.Я импортирую этот CSV-файл в таблицу Excel.

Во входном XML-файле. Если в узле несколько значений, объедините их в одну строку.

Входной XML-файл описан ниже.

    <?xml version="1.0" encoding="ISO-8859-1"?>

    <?xml-stylesheet type="text/xsl" href="ForumCsv.xsl"?>

    <Inventory>
      <Line>
       <LineNumber>line</LineNumber>
       <Description>desc</Description>
       <Matrix>quan</Matrix>
       <Matrix>quan1</Matrix> <!-- added -->
       <Date>date</Date>
      </Line>
      <Line>
       <LineNumber>1</LineNumber>
       <Description>Oak chairs</Description>
       <Matrix>5</Matrix>
       <Matrix>20</Matrix> <!-- added -->
       <Matrix>16</Matrix> <!-- added -->
       <Date>31 Dec 2004</Date>
      </Line>
      <Line>
       <LineNumber>2</LineNumber>
       <Description>Dining tables</Description>
       <Matrix>
        <SubComp>100</SubComp>
        <SubComp>300</SubComp>
        </Matrix>
       <Date>31 Dec 2004</Date>
      </Line>
      <Line>
       <LineNumber>3</LineNumber>
       <Description>Folding chairs</Description>
       <Matrix>4</Matrix>
       <Date>29 Dec 2004</Date>
      </Line>
      <Line>
       <LineNumber>4</LineNumber>
       <Description>Couch</Description>
       <Matrix>1</Matrix>
       <Date>31 Dec 2004</Date>
      </Line>
     </Inventory>

Ожидаемый вывод такой, как показано ниже.

line|desc|quan,quan1|date
1|Oak chairs|5,20,16| Dec 2004
2|Dining tables|100,300|31 Dec 2004
3|Folding chairs|4|29 Dec 2004

Исходный код, который я написал, приведен ниже.

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" indent="yes" encoding="ISO-8859-1" omit-xml-declaration="yes"/>
    <xsl:strip-space  elements="*"/>

    <xsl:template name="Newline"><xsl:text>
    </xsl:text></xsl:template>

    <xsl:template match="Inventory">
      <xsl:apply-templates select="Line"/>
    </xsl:template>

    <xsl:template match="Line">
      <xsl:for-each select="*">  
        <!-- THIS IS WHERE I need help. I aim to put a test condition where I wish to identify sibling nodes .
             If sibling nodes are found then dont use '|', but use ';'
             Also I want to paramterize the delimiter

        <xsl:test ????? > 
           <xsl:value-of select="concat(substring(';',1,position()-1),.)"/> 
        </xsl:template>
        -->
       <xsl:value-of select="."/>      
        <xsl:if test="position() != last()">
        <xsl:value-of select="'|'"/>
       </xsl:if>
     </xsl:for-each>
      <xsl:text>&#xd;&#xa;</xsl:text>
     </xsl:template>

    </xsl:stylesheet>

Разделителями являются "|"а также ",".Но я бы хотел их параматизировать.

Также код должен быть общим.Если добавлено более одного элемента, вывод должен быть таким же, то есть «|»или "," с разделителями.Нет жесткого кодирования узлов

Ответы [ 3 ]

1 голос
/ 23 декабря 2011

Это полное, короткое и простое (стиль push, без явных условных инструкций) решение XSLT 1.0 :

<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="pSubItemSeparator" select="','"/>
 <xsl:param name="pItemSeparator" select="'|'"/>

 <xsl:template match="Line">
  <xsl:apply-templates/>
  <xsl:text>&#xA;</xsl:text>
 </xsl:template>

 <xsl:template match="Line/*[1]">
  <xsl:value-of select="."/>
 </xsl:template>

 <xsl:template match=
  "Line/*[position() >1]
  |
   Line/*/*[1]
  ">
  <xsl:value-of select="concat($pItemSeparator, .)"/>
 </xsl:template>

  <xsl:template priority="2" match=
   "Line/*[name()
          =
           name(preceding-sibling::*[1])]
   |
    Line/*/*[position() > 1]
   ">
  <xsl:value-of select="concat($pSubItemSeparator, .)"/>
 </xsl:template>

 <xsl:template match="Line/*[*]">
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<Inventory>
    <Line>
        <LineNumber>line</LineNumber>
        <Description>desc</Description>
        <Matrix>quan</Matrix>
        <Matrix>quan1</Matrix>
        <!-- added -->
        <Date>date</Date>
    </Line>
    <Line>
        <LineNumber>1</LineNumber>
        <Description>Oak chairs</Description>
        <Matrix>5</Matrix>
        <Matrix>20</Matrix>
        <!-- added -->
        <Matrix>16</Matrix>
        <!-- added -->
        <Date>31 Dec 2004</Date>
    </Line>
    <Line>
        <LineNumber>2</LineNumber>
        <Description>Dining tables</Description>
        <Matrix>
            <SubComp>100</SubComp>
            <SubComp>300</SubComp>
        </Matrix>
        <Date>31 Dec 2004</Date>
    </Line>
    <Line>
        <LineNumber>3</LineNumber>
        <Description>Folding chairs</Description>
        <Matrix>4</Matrix>
        <Date>29 Dec 2004</Date>
    </Line>
    <Line>
        <LineNumber>4</LineNumber>
        <Description>Couch</Description>
        <Matrix>1</Matrix>
        <Date>31 Dec 2004</Date>
    </Line>
</Inventory>

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

line|desc|quan,quan1|date
1|Oak chairs|5,20,16|31 Dec 2004
2|Dining tables|100,300|31 Dec 2004
3|Folding chairs|4|29 Dec 2004
4|Couch|1|31 Dec 2004
0 голосов
/ 23 декабря 2011

Вот решение XSLT 2.0, которое можно использовать с процессорами XSLT 2.0, например Saxon 9 или AltovaXML или XQSharp :

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

  <xsl:param name="lf" select="'&#10;'"/>
  <xsl:param name="is" select="','"/>
  <xsl:param name="cs" select="'|'"/>

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

  <xsl:template match="/*/*">
    <xsl:if test="position() gt 1">
      <xsl:value-of select="$lf"/>
    </xsl:if>
    <xsl:for-each-group select="*" group-adjacent="node-name(.)">
      <xsl:if test="position() gt 1">
        <xsl:value-of select="$cs"/>
      </xsl:if>
      <xsl:value-of select="current-group()/descendant-or-self::*[not(*)]" separator="{$is}"/>
    </xsl:for-each-group>
  </xsl:template>

</xsl:stylesheet>
0 голосов
/ 23 декабря 2011

попробуйте это:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" encoding="ISO-8859-1" omit-xml-declaration="yes"/>
<xsl:strip-space  elements="*"/>

<xsl:param name="csvsep" select="'|'"/>
<xsl:param name="datasep" select="','"/>


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

<xsl:template name="Newline"><xsl:text>
</xsl:text></xsl:template>


<xsl:template match="Line/*">
    <xsl:value-of select="concat(., $csvsep)"/>
</xsl:template>
<xsl:template match="Line/*[./*]">
    <xsl:apply-templates select="./*"/>
    <xsl:value-of select="$csvsep"/>
</xsl:template>

<xsl:template match="Line/*[position()=last()]">
    <xsl:value-of select="."/>
    <xsl:text>&#xd;&#xa;</xsl:text>
</xsl:template>
<xsl:template match="Line/*[position()=last() and ./*]">
    <xsl:apply-templates select="./*"/>
    <xsl:text>&#xd;&#xa;</xsl:text>
</xsl:template>

<xsl:template match="Line/*[name(./following-sibling::*)=name(.)]">
    <xsl:value-of select="concat(., $datasep)"/>      
</xsl:template>


<xsl:template match="Line/*/*">
    <xsl:value-of select="concat(., $datasep)"/>
</xsl:template>

<xsl:template match="Line/*/*[position()=last()]">
    <xsl:value-of select="."/>      
</xsl:template>



</xsl:stylesheet>

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

в основном все шаблоны соответствуют детям или внукамLine элемент.Предполагается, что внуки братьев и сестер являются частью одного поля CSV.Последний элемент на любом уровне обрабатывается специально.обратите внимание, что порядок шаблонов значительный.

это решение работает с процессорами xslt 1.0.

надеюсь, это поможет, Carsten

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