Gern stehe ich zur fachlichen Unterstützung in XML-Technologien, C#.NET, VisualBasic.NET und Java zur Verfügung. Sprechen Sie mich einfach an: Mail oder ☎ 0151 . 750 360 61


XProc

XProc

XProc

➪ XProc bietet komfortable Möglichkeiten, ganze Konvertierungsstrecken bedingungsgesteuert samt Validierungen zu definieren. Dabei kann sowohl XSLT als auch XQuery zum Einsatz kommen.

Auf dieser Seite:

pic/xproc.png

Das folgende XProc-Script


<?xml version="1.0" encoding="UTF-8"?> 
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" 
  xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0"> 
  <p:input port="source" sequence="true"/> 
  <p:input port="parameters" kind="parameter"/>
  <p:output port="result" sequence="true"/> 
  <p:validate-with-xml-schema>
    <p:input port="schema">
      <p:document href="Ort_Elemente.xsd"/>
    </p:input>
  </p:validate-with-xml-schema>
  <p:xslt> 
    <p:input port="stylesheet"> 
      <p:document href="p3_XML2XML_2Gruppen.xsl"/> 
    </p:input> 
  </p:xslt> 
  <p:validate-with-xml-schema>
    <p:input port="schema">
      <p:document href="ZweiGruppen.xsd"/>
    </p:input>
  </p:validate-with-xml-schema>
  <p:xquery>
    <p:input port="query">
      <p:data href="wgqu1.xquery"/>
    </p:input>
  </p:xquery>
  <p:xquery>
    <p:input port="query">
      <p:data href="wgqu_E2A.xquery"/>
    </p:input>
  </p:xquery>
</p:declare-step> 

Nach der Validierung des ursprünglichen Inputs startet p3_XML2XML_2Gruppen.xsl mit der ersten Transformation.


<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" version="1.0" 
      encoding="UTF-8" indent="yes"/>
 <xsl:template match="/">
  <Root>
   <A>
    <xsl:for-each 
         select="/Orte/Ort/Mensch[Gehalt &gt;= sum(Kauf/Gesamt)]">      
     <xsl:call-template name="shPerson"/>
    </xsl:for-each>
   </A>
   <B>
    <xsl:for-each 
         select="/Orte/Ort/Mensch[Gehalt &lt; sum(Kauf/Gesamt)]">      
     <xsl:call-template name="shPerson"/>
    </xsl:for-each>
   </B>
  </Root>
 </xsl:template> 
 <xsl:template name="shPerson">
  <Person>
   <Nr><xsl:value-of select="position()"/></Nr>
   <NN><xsl:value-of select="name"/></NN>
   <VN><xsl:value-of select="vorname"/></VN>
   <WO><xsl:value-of select="../name"/></WO>
   <EK><xsl:value-of select="Gehalt"/></EK>
   <AG><xsl:value-of select="sum(Kauf/Gesamt)"/></AG>
  </Person>
 </xsl:template>
</xsl:stylesheet>

Das Ergebnis dieser XSL-Transformation (also das erste Zwischenergebnis der Konvertierungsstrecke) ist (kurz gefasst):


<Root>
 <A>
  <Person>
   <Nr>1</Nr>
   <NN>Nixlos</NN>
   <VN>Nicole</VN>
   <WO>Neustadt</WO>
   <EK>1234.56</EK>
   <AG>961.6300000000001</AG>
  </Person>
 </A>
 <B>
  <Person>
   <Nr>1</Nr>
   <NN>Holzflos</NN>
   <VN>Hugo</VN>
   <WO>Neustadt</WO>
   <EK>234.56</EK>
   <AG>3563.1400000000003</AG>
  </Person>
  <Person>
   <Nr>4</Nr>
   <NN>Rhodos</NN>
   <VN>Rudi</VN>
   <WO>Darmstadt</WO>
   <EK>333.33</EK>
   <AG>740.6999999999999</AG>
  </Person>
  <Person>
   <Nr>5</Nr>
   <NN>Schlaflos</NN>
   <VN>Susi</VN>
   <WO>Kapstadt</WO>
   <EK>321</EK>
   <AG>808.13</AG>
  </Person>
  <Person>
   <Nr>7</Nr>
   <NN>Leinenlos</NN>
   <VN>Liane</VN>
   <WO>Kapstadt</WO>
   <EK>135</EK>
   <AG>964.0699999999999</AG>
  </Person>
 </B>
</Root>

Im nächsten Schritt wird dieses Ergebnis gegen ein XML-Schema validiert. Wenn hier kein Fehler auftaucht, übernimmt das externe XQuery-Script wgqu1.xquery das vorige Zwischenergebnis als XML-Input und verarbeitet es weiter, indem es alle Personen aus der Gruppe B herausfiltert, deren Ausgaben größer sind als die Einnahmen:


<erg>
{
  for $m in /Root/B/Person
  where $m/AG > $m/EK 
  return 
    <Problem EK="{$m/EK/text()}" 
             AG="{$m/AG/text()}">
      <Vorname>{$m/VN/text()}</Vorname>
      <Nachname>{$m/NN/text()}</Nachname>
      <Stadt>{$m/WO/text()}</Stadt>
    </Problem>
}
</erg>

Das Ergebnis dieser XQuery-basierten Transformation ist dann:


<erg>
  <Problem EK="234.56" AG="3563.1400000000003">
    <Vorname>Hugo</Vorname>
    <Nachname>Holzflos</Nachname>
    <Stadt>Neustadt</Stadt>
  </Problem>
  <Problem EK="333.33" AG="740.6999999999999">
    <Vorname>Rudi</Vorname>
    <Nachname>Rhodos</Nachname>
    <Stadt>Darmstadt</Stadt>
  </Problem>
  <Problem EK="321" AG="808.13">
    <Vorname>Susi</Vorname>
    <Nachname>Schlaflos</Nachname>
    <Stadt>Kapstadt</Stadt>
  </Problem>
  <Problem EK="135" AG="964.0699999999999">
    <Vorname>Liane</Vorname>
    <Nachname>Leinenlos</Nachname>
    <Stadt>Kapstadt</Stadt>
  </Problem>
</erg>

Das externe XQuery-Script wgqu_E2A.xquery nimmt den XML-Input aus der vorherigen XQuery-Transformation entgegen und verarbeitet ihn weiter, indem es


<ERGEBNIS>
{
  (: definiert die ganze Sequenz :)
  let $root := /erg/Problem 
  (: Vorsortierung loop ueber die Sequenz in tempvar :)
  let $tempvar :=   for $m at $p in $root 
    let $diff := (xs:decimal($m/@EK) - xs:decimal($m/@AG))
    order by $diff descending
    return $m
  (: Nummerierung loop ueber tempvar :)
  (: nr gibt Nummerierung vor der Sortierung :)
  (: pos und nr2 gibt Nummerierung nach der Sortierung :)
  for $m at $p in $tempvar 
    let $diff := (xs:decimal($m/@EK) - xs:decimal($m/@AG))
    return 
      <Problem>
      {attribute nr {index-of($root, $m)}}
      {attribute nr2 {index-of($tempvar, $m)}}
      {attribute pos {$p}}
      {attribute diff {$diff}}
      {for $a in $m/attribute::* return $a}
      {
      for $c in $m/child::* 
      return attribute {$c/local-name()} {$c/text()}
      } 
      </Problem>
}
</ERGEBNIS>

Das Endergebnis lautet dann:


<ERGEBNIS>
  <Problem nr="2" nr2="1" pos="1" 
           diff="-407.3699999999999" 
           EK="333.33" 
           AG="740.6999999999999" 
           Vorname="Rudi" 
           Nachname="Rhodos" 
           Stadt="Darmstadt"/>
  <Problem nr="3" nr2="2" pos="2" 
           diff="-487.13" 
           EK="321" 
           AG="808.13" 
           Vorname="Susi" 
           Nachname="Schlaflos" 
           Stadt="Kapstadt"/>
  <Problem nr="4" nr2="3" pos="3" 
           diff="-829.0699999999999" 
           EK="135" 
           AG="964.0699999999999" 
           Vorname="Liane" 
           Nachname="Leinenlos" 
           Stadt="Kapstadt"/>
  <Problem nr="1" nr2="4" pos="4" 
           diff="-3328.5800000000003" 
           EK="234.56" 
           AG="3563.1400000000003" 
           Vorname="Hugo" 
           Nachname="Holzflos" 
           Stadt="Neustadt"/>
</ERGEBNIS>

Sequenz transformieren und filtern

In habe ich in map / filter / reduce ein Beispiel gezeigt, wie die Werte einer Integer-Sequenz (stream) quadriert und anschliessend gefiltert (alle Werte, die ohne Rest durch 3 teilbar sind) werden. Dasselbe Beispiel habe ich in in anderer Weise gelöst. Um die Funktionsweise der XProc-Pipeline zu demonstrieren, versuche ich dasselbe hier noch einmal.


<p:declare-step 
  xmlns:p="http://www.w3.org/ns/xproc"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0">  
  <p:input port="source" sequence="true">
    <p:inline><null/></p:inline>
  </p:input>
  <p:input port="parameters" kind="parameter"/>
  <p:output port="result" sequence="true"/>
  <p:xslt name="generiere_Ursprungszahlen">
    <p:input port="stylesheet">
      <p:inline>
        <xsl:stylesheet version="2.0" exclude-result-prefixes="xs c">
          <xsl:output method="xml" indent="yes"/>
          <xsl:variable name="vstart" select="5"/>
          <xsl:variable name="vende" select="19"/>
          <xsl:template match="/">
            <ergebnis>
              <xsl:for-each 
                   select="(xs:integer($vstart) to xs:integer($vende))">
              <wert><xsl:value-of select="."/></wert>
            </xsl:for-each>
            </ergebnis>
          </xsl:template>
        </xsl:stylesheet>
      </p:inline>
    </p:input>    
  </p:xslt>
  <p:store href="step1.xml" method="xml" indent="true" />
  <p:xslt name="quadriere_Ursprungszahlen">
    <p:input port="source"> 
      <p:pipe step="generiere_Ursprungszahlen" port="result"/> 
    </p:input>
    <p:input port="stylesheet">
      <p:inline>
        <xsl:stylesheet version="2.0"
          exclude-result-prefixes="xs c">
          <xsl:output method="xml" indent="yes"/>
          <xsl:template match="/ergebnis">
            <ergebnis2>
              <xsl:for-each select="//wert">
                <wert2><xsl:value-of select=". * ."/></wert2>
              </xsl:for-each>
            </ergebnis2>
          </xsl:template>
        </xsl:stylesheet>
      </p:inline>
    </p:input>    
  </p:xslt>
  <p:store href="step2.xml" method="xml" indent="true" />
  <p:xslt name="filtere_quadrierte_Ursprungszahlen">
    <p:input port="source"> 
      <p:pipe step="quadriere_Ursprungszahlen" port="result"/> 
    </p:input>
    <p:input port="stylesheet">
      <p:inline>
        <xsl:stylesheet version="3.0"
          exclude-result-prefixes="xs c">
          <xsl:output method="xml" indent="yes"/>
          <xsl:template match="/ergebnis2">
            <ergebnis3>
              <!-- Alternativ:
              <xsl:for-each select="//wert2[. mod 3 = 0]">
                <wert3><xsl:value-of select="."/></wert3>
              </xsl:for-each>
              -->
              <xsl:for-each select="filter(
                //wert2, 
                function($a as xs:integer) {$a mod 3 = 0})">
                <wert3><xsl:value-of select="."/></wert3>
              </xsl:for-each>
            </ergebnis3>
          </xsl:template>
        </xsl:stylesheet>
      </p:inline>
    </p:input>    
  </p:xslt>
  <p:store href="step3.xml" method="xml" indent="true" />
  <p:xslt name="summiere_gefilterte_quadrierte_Ursprungszahlen">
    <p:input port="source"> 
      <p:pipe step="filtere_quadrierte_Ursprungszahlen" port="result"/> 
    </p:input>
    <p:input port="stylesheet">
      <p:inline>
        <xsl:stylesheet version="2.0"
          exclude-result-prefixes="xs c">
          <xsl:output method="xml" indent="yes"/>
          <xsl:template match="/ergebnis3">
            <ergebnis4>
              <xsl:value-of select="sum(//wert3)"/>              
            </ergebnis4>
          </xsl:template>
        </xsl:stylesheet>
      </p:inline>
    </p:input>    
  </p:xslt>
</p:declare-step>

Das Resultat dieser Pipeline lautet:


<ergebnis4>810</ergebnis4>

XML Streaming mit XProc

Angenommen, Sie müssen ein sehr großes XML-Dokument verarbeiten.

Nehmen Sie an, das XML-Input Dokument (Datei: Ort_Elemente.xml) hätte (neben zahlreichen weiteren Einträgen) diesen Aufbau:


<Orte>
  <Ort>
    <id>3</id>
    <name>Kapstadt</name>    
    <Mensch>
      <id>13</id>
      <name>Muehelos</name>
      <vorname>Martin</vorname>
      <Gehalt>222</Gehalt>      
      <idOrt>3</idOrt>
      <Kauf>
        <idMensch>13</idMensch>
        <anzahl>7</anzahl>
        <bez>Hemd</bez>
        <preis>12.99</preis>
        <Gesamt>90.93</Gesamt>
      </Kauf>
      <Kauf>
        <idMensch>13</idMensch>
        <anzahl>4</anzahl>
        <bez>Hemd</bez>
        <preis>12.99</preis>
        <Gesamt>51.96</Gesamt>
      </Kauf>
    </Mensch>
    <Mensch>
      <id>14</id>
      <name>Leinenlos</name>
      <vorname>Liane</vorname>
      <Gehalt>135</Gehalt>
      <idOrt>3</idOrt>
      <Kauf>
        <idMensch>14</idMensch>
        <anzahl>8</anzahl>
        <bez>Hose</bez>
        <preis>25.99</preis>
        <Gesamt>207.92</Gesamt>
      </Kauf>
      <Kauf>
        <idMensch>14</idMensch>
        <anzahl>5</anzahl>
        <bez>Schuhe</bez>
        <preis>151.23</preis>
        <Gesamt>756.15</Gesamt>
      </Kauf>
    </Mensch>
  </Ort>
</Orte>

Dann können Sie mit Streaming so verfahren:


<p:declare-step 
  xmlns:p="http://www.w3.org/ns/xproc"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:c="http://www.w3.org/ns/xproc-step" version="1.0">  
  <p:load href="Ort_Elemente.xml"/>
  <p:for-each>
    <p:iteration-source 
       select="/Orte/Ort/Mensch[Gehalt &lt; 230]"/>      
    <p:xslt>
      <p:input port="stylesheet">
        <p:inline>
          <xsl:stylesheet 
               version="2.0" 
               exclude-result-prefixes="xs c">
            <xsl:output method="xml" indent="yes"/>
            <xsl:template match="*">
              <Person>
                <NN><xsl:value-of select="name"/></NN>
                <VN><xsl:value-of select="vorname"/></VN>                
                <WO><xsl:value-of select="../name"/></WO>
                <AG>
                  <xsl:for-each select="Kauf">
                    <Artikel bez="{bez}" anzahl="{anzahl}"/>
                  </xsl:for-each>
                </AG>
              </Person>              
            </xsl:template>
          </xsl:stylesheet>
        </p:inline>
      </p:input>
      <p:input port="parameters">
        <p:empty/>
      </p:input>
    </p:xslt>
  </p:for-each>
  <p:wrap-sequence wrapper="RESULT"/>  
  <p:store href="Ort_Elemente_XProc.xml"/>
</p:declare-step>

Im Ergebnis (Datei: Ort_Elemente_XProc.xml) können Sie feststellen, dass die p:iteration-source lediglich die Mensch-Nodes auswertet, der Bezug zu deren Parent-Node Ort geht verloren. Der XPath-Ausdruck ../name ergibt kein Resultat.


<RESULT>
    <Person>
        <NN>Muehelos</NN>
        <VN>Martin</VN>
        <EK>222</EK>
        <WO/>
        <AG>
            <Artikel bez="Hemd" anzahl="7"/>
            <Artikel bez="Hemd" anzahl="4"/>
        </AG>
    </Person>
    <Person>
        <NN>Leinenlos</NN>
        <VN>Liane</VN>
        <EK>135</EK>
        <WO/>
        <AG>
            <Artikel bez="Hose" anzahl="8"/>
            <Artikel bez="Schuhe" anzahl="5"/>
        </AG>
    </Person>
</RESULT>

wg / 24. November 2018



Fragen? Anmerkungen? Tipps?

Bitte nehmen Sie Kontakt zu mir auf.




CSV



Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/XProc.html