Gunnar Bittersmann: Pseudoattribut-Knoten von PI-Knoten

Hello out there!

Wie adressiert man die Pseudoattribut-Knoten eines Verarbeitungsanweisung-Knotens? Mit '@*' will mir das nicht gelingen.

Ich möchte die Quelle

<?foo bar="baz"?>  
<quz/>

transformieren in

<quz>  
  <foo bar="baz"/>  
</quz>

See ya up the road,
Gunnar

--
„Und [dieses Forum] soll […] auch ein Fachforum bleiben und kein Psychologieforum werden.“ (Kirsten Evers)
  1. Hallo Gunnar,

    Wie adressiert man die Pseudoattribut-Knoten eines Verarbeitungsanweisung-Knotens?

    Gar nicht, weil's keine Knoten sind. Eine Verarbeitungsanweisung <?foo bar="baz"?> wird von einem XML-Parser als ein EINZIGER Knoten des Typs "PI" mit Namen "foo" und Inhalt 'bar="baz"' interpretiert.

    Dir bleibt wohl nichts anderes, als die Attributwerte mit Stringfunktionen selbst zu parsen (was in XSLT 1.0 SEHR eklig wird).

    Viele Grüße,
    Christian

    1. Hello out there!

      Gar nicht, weil's keine Knoten sind. Eine Verarbeitungsanweisung <?foo bar="baz"?> wird von einem XML-Parser als ein EINZIGER Knoten des Typs "PI" mit Namen "foo" und Inhalt 'bar="baz"' interpretiert.

      Wie unangenehm. Danke für den Hinweis.

      Dir bleibt wohl nichts anderes, als die Attributwerte mit Stringfunktionen selbst zu parsen (was in XSLT 1.0 SEHR eklig wird).

      Naja, geht so.

      Die Pseudoattribute, die vorkommen können, sind wenige und namentlich bekannt.

      Da tut’s fürs erste

      <xsl:value-of select="substring-before(substring-after(current(),'bar=&quot;'), '&quot;')"/>

      Eklig wird’s, weil statt doppelten auch einfache Anführungszeichen stehen können und beliebig viel Whitespace um das '=' stehen kann:

      <?foo bar = 'baz' ?>

      Ein Stylesheet sollte nicht nur eine kanonische Form erkennen, wenn XML auch anderes erlaubt.

      See ya up the road,
      Gunnar

      --
      „Und [dieses Forum] soll […] auch ein Fachforum bleiben und kein Psychologieforum werden.“ (Kirsten Evers)
      1. Hello out there!

        Die Pseudoattribute, die vorkommen können, sind wenige und namentlich bekannt.

        Müssen sie gar nicht. „Um Rekursion zu verstehen, muss man zunächst Rekursion verstehen.“ [Cheatah]

        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
          <xsl:output  
            xsl:method="xml"  
            xsl:encoding="UTF-8"  
            xsl:standalone="no"  
            xsl:indent="yes"  
          />  
          <xsl:template match="/">  
            <quz>  
              <xsl:apply-templates/>  
            </quz>  
          </xsl:template>  
          
          <xsl:template match="processing-instruction('foo')">  
            <foo>  
              <xsl:call-template name="getNextPseudoattribute">  
                <xsl:with-param name="string" select="current()"/>  
              </xsl:call-template>  
            </foo>  
          </xsl:template>  
          
          <xsl:template name="getNextPseudoattribute">  
            <xsl:param name="string"/>  
            <xsl:variable name="pseudoattributeName" select="normalize-space(substring-before($string, '='))"/>  
            <xsl:variable name="substringAfterEqualSign" select="normalize-space(substring-after($string, '='))"/>  
            <xsl:variable name="startsWithApos" select='starts-with($substringAfterEqualSign, "&apos;")'/>  
            <xsl:if test="starts-with($substringAfterEqualSign, '&quot;') or $startsWithApos">  
              <xsl:variable name="limiter" select="substring($substringAfterEqualSign, 1, 1)"/>  
              <xsl:variable name="substringAfterLimiter" select="substring($substringAfterEqualSign, 2)"/>  
              <xsl:variable name="pseudoattributeValue" select="substring-before($substringAfterLimiter, $limiter)"/>  
              <xsl:attribute name="{$pseudoattributeName}"><xsl:value-of select="$pseudoattributeValue"/></xsl:attribute>  
              <xsl:call-template name="getNextPseudoattribute">  
                <xsl:with-param name="string" select="substring-after($substringAfterLimiter, $limiter)"/>  
                <xsl:with-param name="alternate" select="true()"/>  
              </xsl:call-template>  
            </xsl:if>  
          </xsl:template>  
        </xsl:stylesheet>
        

        „Aber wem erzähle ich das? Dem wissend zwinkernden? Wahrscheinlich doch nur dem Archiv.“ [at]

        Eklig wird’s, weil statt doppelten auch einfache Anführungszeichen stehen können

        Tricky ist die Sache mit '"' und "'". Eine Maskierung hab ich nicht hinbekommen; da fiel mir nichts Besseres ein als das Ergebnis des einen Tests in der Variablen startsWithApos abzulegen. Geht das auch ohne? Lassen sich die beiden Zeilen

            <xsl:variable name="startsWithApos" select='starts-with($substringAfterEqualSign, "&apos;")'/>  
            <xsl:if test="starts-with($substringAfterEqualSign, '&quot;') or $startsWithApos">
        

        in nur mit einem 'xsl:if' schreiben?

        See ya up the road,
        Gunnar

        --
        „Und [dieses Forum] soll […] auch ein Fachforum bleiben und kein Psychologieforum werden.“ (Kirsten Evers)
        1. Hallo Gunnar,

          „Aber wem erzähle ich das? Dem wissend zwinkernden? Wahrscheinlich doch nur dem Archiv.“

          Ich finde, dieser Thread wäre ein guter Kandidat für die Forumsauslese. Hättest Du etwas dagegen, wenn der Thread inklusive Deiner Lösung im Weblog weiterverarbeitet wird?

          Eklig wird’s, weil statt doppelten auch einfache Anführungszeichen stehen können

          Tricky ist die Sache mit '"' und "'". Eine Maskierung hab ich nicht hinbekommen; da fiel mir nichts Besseres ein als das Ergebnis des einen Tests in der Variablen startsWithApos abzulegen. Geht das auch ohne? Lassen sich die beiden Zeilen

          <xsl:variable name="startsWithApos" select='starts-with($substringAfterEqualSign, "&apos;")'/>

          <xsl:if test="starts-with($substringAfterEqualSign, '&quot;') or $startsWithApos">

          
          >   
          > in nur mit einem 'xsl:if' schreiben?  
            
          Ja, das geht:  
            
          `<xsl:if test="starts-with($substringAfterEqualSign, '&quot;') or starts-with($substringAfterEqualSign, &quot;'&quot;)">`{:.language-xml}  
            
          In XPath 2.0 (NICHT 1.0, das fliegt auf die Schnauze!) ist auch noch folgendes möglich:  
            
          `<xsl:if test="starts-with($substringAfterEqualSign, '&quot;') or starts-with($substringAfterEqualSign, '''')">`{:.language-xml}  
            
          Das kann man z.B. in Funktion sehen, wenn man Dein Stylesheet durch Saxon jagt (Saxon kann XPsth 2.0). Da Saxon allerdings etwas strenger ist, musst Du das Namespace-Prefix vor den Attributen in Deinem xsl:output jedoch weglassen, der XSLT-Standard schreibt für Attribute nämlich keinen Namespace vor:  
            
          `<xsl:output method="xml" encoding="UTF-8" standalone="no" indent="yes" />`{:.language-xml}  
            
          Viele Grüße,  
          Christian  
          
          
          1. Hello out there!

            Ich finde, dieser Thread wäre ein guter Kandidat für die Forumsauslese. Hättest Du etwas dagegen, wenn der Thread inklusive Deiner Lösung im Weblog weiterverarbeitet wird?

            Nein, nur zu.

            Sollte es dann "foo, bar, baz, quz" heißen oder auf den konnkreten Anwendungsfall bezogen werden? Da käme allerdings noch einiges hinzu.

            See ya up the road,
            Gunnar

            --
            „Und [dieses Forum] soll […] auch ein Fachforum bleiben und kein Psychologieforum werden.“ (Kirsten Evers)
            1. Hallo Gunnar,

              Sollte es dann "foo, bar, baz, quz" heißen oder auf den konnkreten Anwendungsfall bezogen werden? Da käme allerdings noch einiges hinzu.

              Nein, das reicht. Ich werde evtl. andere Beispielnamen als "foo", "bar", "baz" oder "quz" nehmen (das ist zu technisch ;-)), aber das dürfte als Weblog-Beitrag IMHO reichen.

              Viele Grüße,
              Christian

              1. Hello out there!

                Sollte es dann "foo, bar, baz, quz" heißen oder auf den konnkreten Anwendungsfall bezogen werden? Da käme allerdings noch einiges hinzu.

                Nein, das reicht.

                Na gut, dann verrate ich (noch) nicht, was ich damit evtl. mal vorhabe. :-b

                Ich werde evtl. andere Beispielnamen als "foo", "bar", "baz" oder "quz" nehmen

                Und bitte "delimiter" statt "limiter". Wie peinlich.

                See ya up the road,
                Gunnar

                --
                „Und [dieses Forum] soll […] auch ein Fachforum bleiben und kein Psychologieforum werden.“ (Kirsten Evers)
        2. Hallo Gunnar,

          <xsl:attribute name="{$pseudoattributeName}"><xsl:value-of select="$pseudoattributeValue"/></xsl:attribute>

          Nochwas: Strenggenommen muss man hier übrigens evtl. vorkommende Entities und NCR unescapen, wenn Du mit dem Code nämlich <?foo bar="baz&lt;foo"?> hast, dann wird das im Attribut später als bar="baz&amp;lt;foo" auftauchen...

          In XPath ist mir jedoch keine Funktion bekannt, die das für einen übernimmt. Andererseits dürfte man wohl fast alle Fälle erschlagen, wenn man manuell &apos;, &quot;, &lt;, &gt; und &amp; ersetzt und sich um NCRs nicht kümmert. In XSLT 1.0 wird das halt ziemlich aufwändig, in XSLT 2.0 gibt's wenigstens direkte Ersetzungsfunktionen dafür.

          Viele Grüße,
          Christian