0

I want to change this code

<?xml version="1.0" encoding="UTF-8"?>
  <xml>
    <file id="Tokw" cast="ind">
            <name>Token wst</name>
        <package>b_3</package>
        <package>c_5</package>
    </file>
    <file id="strlin" cast="bac">
            <name>str line</name>
        <package>b_2</package>
        <package>c_5</package>
            <package>a_2</package>
    </file>

and so on, to this code:

<md_db>
  <ID>1</ID>                        //auto numeration
  <file_id>Tokw</package_id>
  <cast>ind</cast>
  <name>Token wst</name>
  <package>b_3 c_5</package>
</md_db>
<md_db>
  <ID>2</ID>
  <file_id>stlin</file_id>
  <cast>bac</cast>
  <name>str line</name>
  <package>b_2 c_5 a_2</package>
</md_db>

What xsl use to change this XML file? It is breaking up first line file id, and if there are packages it has to collect them up into one node package. There is also ID which doesn't have to be auto numerated, but there have to be one number.

I have this code yet:

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

    <xsl:template match="xml">
        <md_db>
            <ID></ID>
            <file><xsl:value-of select="file id"/></file>
        <cat><xsl:value-of select="cat"/></cat>
            <name><xsl:value-of select="name"/></name>
        <xsl:if test="package">
            <package><xsl:value-of select="package"/></package>
        </xsl:if>
        </md_db>
    </xsl:template>
</xsl:stylesheet>
Levvy
  • 1,100
  • 1
  • 10
  • 21
  • Do you have a **specific** question? Why don't you post the XSLT code you have so far, and point out what doesn't work the way you want. – michael.hor257k May 17 '14 at 08:41
  • I don't exactly know xsl. just basics, so don't know how to implement advanced lines – Levvy May 17 '14 at 10:48

1 Answers1

2

Often, in problems such as this, you would start off with the Identity Template.

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

On its own, it just copies the nodes as-is, and you would then just write templates for the nodes you did need to change. In your case, it is nearly all of them, but it is still worth using.

So, for example, to transform a file element to an md_db element, the template would look lije this

  <xsl:template match="file">
    <md_db>
      <ID><xsl:number /></ID>
      <xsl:apply-templates select="@*|node()"/>
    </md_db>
  </xsl:template>

(Note the use of xsl:number to do the enumeration)

And to turn the id attribute on the file element to a file_id element, you would do this:

  <xsl:template match="file/@id">
    <file_id>
      <xsl:value-of select="."/>
    </file_id>
  </xsl:template>

As for the package element, you could have a template that matches the first package element only

  <xsl:template match="package[1]">

It looks like you are using XSLT 2.0, in which case you can output the current package and following package elements quite easily

    <package>
      <xsl:value-of select=".|following-sibling::package" separator=" "/>
    </package>

All else that what be needed is another template to stop the following package elements being picked up by the identity transform and output twice:

  <xsl:template match="package"/>

Note that XSLT will give priority to the more specific template where two templates may match the same node.

Try this XSLT:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="file">
    <md_db>
      <ID><xsl:number /></ID>
      <xsl:apply-templates select="@*|node()"/>
    </md_db>
  </xsl:template>

  <xsl:template match="file/@cast">
    <cast>
      <xsl:value-of select="."/>
    </cast>
  </xsl:template>

  <xsl:template match="file/@id">
    <file_id>
      <xsl:value-of select="."/>
    </file_id>
  </xsl:template>

  <xsl:template match="package[1]">
    <package>
      <xsl:value-of select=".|following-sibling::package" separator=" "/>
    </package>
  </xsl:template>

  <xsl:template match="package"/>

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

EDIT: If you are not able to use XSLT 2.0, you can just change the Package[1] template to use a simple xsl:for-each instead:

  <xsl:template match="package[1]">
    <package>
      <xsl:value-of select="."/>
      <xsl:for-each select="following-sibling::package">
        <xsl:value-of select="concat(' ', .)"/>
      </xsl:for-each>
    </package>
  </xsl:template>
Tim C
  • 70,053
  • 14
  • 74
  • 93
  • what is alternative in 1.0 because I think http://www.freeformatter.com/xsl-transformer.html doesn't support 2.0 – Levvy May 17 '14 at 18:37
  • I've added an edit to my answer to show an alternative method that works in XSLT 1.0. – Tim C May 17 '14 at 19:08
  • Thanks a lot. Also for page. – Levvy May 17 '14 at 19:58
  • But how to use it, when I have more multiple nodes except , such as and ? – Levvy May 17 '14 at 20:11
  • It might be worth asking a new question if you can't work it out, otherwise this question is going to get a bit over-complicated. It should be possible to just copy the "Package" template though, and replace it with the name of other node that is repeated. – Tim C May 17 '14 at 21:28
  • Ok. I have made it. After copy I had to change number [2] [3] and so on. – Levvy May 18 '14 at 18:56