# String in XSLT teilen



## RicoTT (10. Feb 2016)

Hallo zusammen!

Ich möchte eine XML mit folgendem Node einlesen:

```
<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1', 'COM2', 'COM2']</PropertyValue>
</Property>
```
Daraus soll dann für jedes Element unter Property Value 
ein neues Item erstellt werden.

```
<ITEM>
      <NAME>COM1</NAME>
</ITEM>
<ITEM>
      <NAME>COM2</NAME>
</ITEM>
<ITEM>
      <NAME>COM3</NAME>
</ITEM>
```

Mein Problem liegt jetzt darin den eingelesenen String zu teilen.
Habe zwar einige Beispiele gefunden, allerdings haben mir diese noch nicht so richtig helfen können.
http://www.codeproject.com/Questions/66195/Need-XSL-to-split-a-string-from-XML-input

Wäre super, wenn mir jemand helfen könnte.

Danke!


----------



## kneitzel (10. Feb 2016)

Also Du hast einen Link zur Lösung ja schon gepostet. Und wir hatten vor Kurzem ja schon einen Thread bezüglich Transformationen, bei dem ich wohl auch Links zu Tutorials gepostet habe. (Wenn nicht: Google bringt da ja sehr viele Treffer.)

Woran scheitert es? Was verstehst Du bei der Problematik bzw. bei der angebotenen Lösung auf der Webseite nicht? Ich sehe im Augenblick keinen Ansatzpunkt, um Dir wirklich effektiv mit Erläuterungen weiter zu helfen.

Ich habe mal einfach ein paar Dinge in der Lösung von dem Thread angepasst und einige Zwischenschritte mit ausgegeben, so dass Du es vielleicht leichter hast, die Lösung nachzuvollziehen.

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

    <xsl:output method="xml" indent="yes"/>

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

    <xsl:template match="Property">
        <xsl:call-template name="SplitLinks">
            <xsl:with-param name="ComList" select="PropertyValue/text()"/>
        </xsl:call-template>
    </xsl:template>

    <!-- recursive template for splitting links -->
    <xsl:template name="SplitLinks">
        <xsl:param name="ComList"/>
        <Call> <xsl:value-of select="$ComList" /> </Call>
        <xsl:variable name="vCountSeparators" select="string-length($ComList) - string-length(translate($ComList,',',''))"/>
        <vCountSeparators> <xsl:value-of select="$vCountSeparators"/> </vCountSeparators>

        <xsl:choose>
            <xsl:when test="$vCountSeparators = 0">
              <ITEM>
                <NAME>
                    <xsl:variable name='t1' select="$ComList" />
                    <xsl:variable name='t2' select='substring-after($t1,"&apos;")' />
                    <xsl:variable name='t3' select='substring-before($t2,"&apos;")' />
                    <T1><xsl:value-of select="$t1"/></T1>
                    <T2><xsl:value-of select="$t2"/></T2>
                    <T3><xsl:value-of select="$t3"/></T3>
                </NAME>
              </ITEM>
            </xsl:when>

            <xsl:when test="$vCountSeparators &gt;= 1">
            <ITEM>
              <NAME>
                    <xsl:variable name='t1' select="substring-before($ComList,',')" />
                    <xsl:variable name='t2' select='substring-after($t1,"&apos;")' />
                    <xsl:variable name='t3' select='substring-before($t2,"&apos;")' />
                    <T1><xsl:value-of select="$t1"/></T1>
                    <T2><xsl:value-of select="$t2"/></T2>
                    <T3><xsl:value-of select="$t3"/></T3>
              </NAME>
            </ITEM>
                <!-- recursive call -->
                <xsl:call-template name="SplitLinks">
                    <xsl:with-param name="ComList" select="substring-after($ComList,',')"/>
                </xsl:call-template>
            </xsl:when>

        </xsl:choose>

     </xsl:template>
</xsl:stylesheet>
```

Also der Aufbau ist generell ganz einfach:
- Globales template (match="/") - Dort habe ich mal einfach ein root Element "result" eingefügt - das kannst Du aber auch raus nehmen. Wichtig ist hier nur, dass wir jetzt das Property Element auswerten wollen. Also sozusagen der Aufruf für das Property Element.
- Property template - Hier wird einfach nur das splitLinks (Name habe ich aus Deinem Link einfach beibehalten. Sollte man noch umbenennen) aufgerufen. Als Parameter wird der Inhalt von PropertyValue übergeben.
- splitLinks template. Dieses wird ja aufgerufen. Damit deutlich wird, was da passiert, gebe ich einfach einmal ein paar Informationen aus. In Call schreibe ich den Parameter und dann gebe ich noch in Count die Anzahl der Separatoren an. (Bei der Lösung ist wichtig, dass in einem String NIE der Separator vorkommt. Das wäre hier fatal.)
- splitLinks ist einfach eine Rekursion. Bei 0 Separatoren wird der Inhalt direkt ausgegeben. Bei mindestens einem Separator, wird der String vor dem Separator ausgegeben und dann splitLinks rekursiv aufgerufen mit dem Text nach dem Separator.
- Die Ausgabe würde hier noch die ' und auch die [ und ] beinhalten. Die schneide ich einfach ab indem ich nur den Text nach und vor dem ' (&apos) ausgebe. Das habe ich hier einfach mit Hilfvariablen t1, t2 und t3 gemacht - das wäre nicht zwingend nötig. Und ich gebe diese Variablen in entsprechenden Elementen aus. 
Zur Vereinfachung sollte man evt ein template Ausgabe erstellen, dass den Text als Parameter bekommt und dass dann die Entfernung der ' durchführt. Dann hat man den Code nicht doppelt. Aber das muss nicht sein. Man kann auch die selects alle ineinander veschachteln um nur einen Ausdruck zu erhalten. Das war jetzt halt nur aufgesplittet um Dir auch die Ergebnisse vor Augen zu führen.

Und wenn man das dann testet (z.B. unter http://xslttest.appspot.com/) dann kann man als XML angeben:

```
<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
  <PropertyName>Zusatzfeld </PropertyName>
  <PropertyValue>['COM1', 'COM2', 'COM2']</PropertyValue>
</Property>
```

und erhält dann:

```
<?xml version="1.0" encoding="UTF-8"?>
<Call>['COM1', 'COM2', 'COM2']</Call>
<vCountSeparators>2</vCountSeparators>
<ITEM>
   <NAME>
      <T1>['COM1'</T1>
      <T2>COM1'</T2>
      <T3>COM1</T3>
   </NAME>
</ITEM>
<Call> 'COM2', 'COM2']</Call>
<vCountSeparators>1</vCountSeparators>
<ITEM>
   <NAME>
      <T1> 'COM2'</T1>
      <T2>COM2'</T2>
      <T3>COM2</T3>
   </NAME>
</ITEM>
<Call> 'COM2']</Call>
<vCountSeparators>0</vCountSeparators>
<ITEM>
   <NAME>
      <T1> 'COM2']</T1>
      <T2>COM2']</T2>
      <T3>COM2</T3>
   </NAME>
</ITEM>
```

Ich hoffe, das hat jetzt ein kleines bisschen geholfen.

Viele Grüße,

Konrad


----------



## RicoTT (11. Feb 2016)

Hallo Konrad,

vielen Dank für die ausführliche Antwort.
Im Laufe des gestrigen Tages war ich dann
auch alleine auf die Lösung gekommen.

Ein Problem habe ich jetzt noch.Die Einzulesende XML variert, das heißt
das Feld PropertyValue kann frei sein, oder 1-3 "COM" enthalten. 
Angepasst daran sollen passend Items erstellt werden.

Also das wäre die Quelle:

```
<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1', 'COM2', 'COM3']</PropertyValue>
</Property>

<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1']</PropertyValue>
</Property>

<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1', 'COM2']</PropertyValue>
</Property>
```

Und daraus soll das hier entstehen:

```
<ITEM>
      <NAME>COM1</NAME>
</ITEM>
<ITEM>
      <NAME>COM2</NAME>
</ITEM>
<ITEM>
      <NAME>COM3</NAME>
</ITEM>
<ITEM>
      <NAME>COM1</NAME>
</ITEM>
<ITEM>
      <NAME>COM1</NAME>
</ITEM>
<ITEM>
      <NAME>COM2</NAME>
</ITEM>
```


----------



## kneitzel (11. Feb 2016)

Das hast Du doch eigentlich in meinem Transform schon. Die ganzen Zusatzausgaben entfernen und ggf. noch ein eigenes template zum Wert anzeigen und schon ist man fertig.

Einziges Problem: Deine Vorgabe ist kein gültiges XML - Du brauchst genau ein root Element. Das habe ich einfach einmal hinzugefügt als "items" (Properties wäre evtl. besser gewesen, aber ich habe es nicht noch einmal angepasst.):

```
<items>
<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1', 'COM2', 'COM3']</PropertyValue>
</Property>

<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1']</PropertyValue>
</Property>

<Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
<PropertyName>Zusatzfeld </PropertyName>
<PropertyValue>['COM1', 'COM2']</PropertyValue>
</Property>
</items>
```

Dann das xslt angepasst:
- im globalen Match muss dann der Ausdruck für die Property Elemente geändert werden. Das ist nun halt nicht mehr Property sondern items/Property
- Die Debug-Ausgaben entfernt
- das template ShowValues eingefügt, damit der Code zum Entfernen der Zeichen bis einschließlich des ' und danach ab dem ' nur noch an einer Stelle zu finden sind.
- template für den Split noch umbenannt (SplitValues statt SplitLinks)
Und damit haben wir dann folgendes xslt:

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

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
            <xsl:apply-templates select="items/Property"/>
    </xsl:template>

    <xsl:template match="Property">
        <xsl:call-template name="SplitValues">
            <xsl:with-param name="ComList" select="PropertyValue/text()"/>
        </xsl:call-template>
    </xsl:template>

    <!-- template to show value -->
    <xsl:template name="ShowValue">
        <xsl:param name="Value"/>
                    <xsl:variable name='t2' select='substring-after($Value,"&apos;")' />
                    <xsl:variable name='t3' select='substring-before($t2,"&apos;")' />
                    <xsl:value-of select="$t3"/>
    </xsl:template>

    <!-- recursive template for splitting links -->
    <xsl:template name="SplitValues">
        <xsl:param name="ComList"/>
        <xsl:variable name="vCountSeparators" select="string-length($ComList) - string-length(translate($ComList,',',''))"/>

        <xsl:choose>
            <xsl:when test="$vCountSeparators = 0">
              <ITEM>
                <NAME>
                   <xsl:call-template name="ShowValue">
                       <xsl:with-param name="Value" select="$ComList"/>
                   </xsl:call-template>
                </NAME>
              </ITEM>
            </xsl:when>

            <xsl:when test="$vCountSeparators &gt;= 1">
            <ITEM>
                <NAME>
                   <xsl:call-template name="ShowValue">
                       <xsl:with-param name="Value" select="substring-before($ComList,',')"/>
                   </xsl:call-template>
                </NAME>
            </ITEM>
                <!-- recursive call -->
                <xsl:call-template name="SplitValues">
                    <xsl:with-param name="ComList" select="substring-after($ComList,',')"/>
                </xsl:call-template>
            </xsl:when>

        </xsl:choose>

     </xsl:template>
</xsl:stylesheet>
```

Und damit erhält man dann tatsächlich:

```
<?xml version="1.0" encoding="UTF-8"?>
<ITEM>
   <NAME>COM1</NAME>
</ITEM>
<ITEM>
   <NAME>COM2</NAME>
</ITEM>
<ITEM>
   <NAME>COM3</NAME>
</ITEM>
<ITEM>
   <NAME>COM1</NAME>
</ITEM>
<ITEM>
   <NAME>COM1</NAME>
</ITEM>
<ITEM>
   <NAME>COM2</NAME>
</ITEM>
```


----------

