2012-11-20 11 views
5

w tym wątku odsyłam do mojego ostatniego wątku Convert XML to CSV using XSLT - dynamic columns.XSLT do generowania kolejnego skryptu XSLT

Skrypt XSLT w odnośnym wątku działa dobrze, ale z dużym dokumentem XML wydajność nie jest dobra. Teraz chcę napisać skrypt XSLT, który wyprowadza inny skrypt XSLT, który wyświetli końcowy plik CSV.

Moje pytanie brzmi: jak napisać pierwszy skrypt XSLT. Wyjście powinno wyglądać następująco:

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

<xsl:template match="/*"> 
    <xsl:text>Name;</xsl:text> 
    <xsl:text>Brother;</xsl:text> 
    <xsl:text>Sister</xsl:text> 
    <-- this part is dynamic --> 
    <xsl:apply-templates select="Person" /> 
</xsl:template> 

<xsl:template match="Person"> 
    <xsl:value-of select="Name" /> 
    <xsl:value-of select="Brother" /> 
    <xsl:value-of select="Sister" /> 
    <-- this part is dynamic too --> 
    <xsl:text>&#10;</xsl:text> 
</xsl:template> 
</xsl:stylesheet> 

Plik XML wejście jest taka sama jak w skieruję wątku:

<Person> 
    <Name>John</Name> 
    <FamilyMembers> 
     <FamilyMember> 
      <Name>Lisa</Name> 
      <Type>Sister</Type> 
     </FamilyMember> 
     <FamilyMember> 
      <Name>Tom</Name> 
      <Type>Brother</Type> 
     </FamilyMember> 
    </FamilyMembers> 
</Person> 
<Person> 
    <Name>Daniel</Name> 
    <FamilyMembers> 
     <FamilyMember> 
      <Name>Peter</Name> 
      <Type>Father</Type> 
     </FamilyMember> 
    </FamilyMembers> 
</Person> 

Tak dla każdego innego elementu typu powinien istnieć jeden wiersz jak:

<xsl:text>Type;</xsl:text> 

w wynikowym skrypcie XSLT.

Może niektórzy z was mają dla mnie dobrą radę. Dziękuję w zaawansowanej!

André

+0

Właściwie nie potrzeba rozwiązania dwu-external-faz. Wszystko, co musisz zrobić, to poprawić wydajność w skali rozwiązania Tima. Prawdziwym pytaniem, które powinieneś zadać, jest optymalizacja na dużą skalę. –

Odpowiedz

11

Aby napisać jedną XSLT, który wyprowadza inny albo trzeba generować elementy wyjściowe przy użyciu <xsl:element>, np

<xsl:element name="xsl:text"> 

lub użyj <xsl:namespace-alias> jeśli chcesz używać dosłownych elementy wynikowe. XSLT spec ma przykład:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:fo="http://www.w3.org/1999/XSL/Format" 
    xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"> 

<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/> 

<xsl:template match="/"> 
    <axsl:stylesheet> 
    <xsl:apply-templates/> 
    </axsl:stylesheet> 
</xsl:template> 

Wszelkie <axsl:...> elementy w arkuszu stylów staną <xsl:...> na wyjściu.

+0

Twoje rozwiązanie najlepiej pasuje do mojego pytania. Używanie axsl jest moim prawdziwym sposobem. Przetestuję później rozwiązanie Seana. – Andre

1

Zamiast rozwiązania opartego na dwóch fazach zewnętrznych (czyli arkuszu stylów, który zapisuje arkusz stylów, który zostanie wykonany), myślę, że lepiej będzie, gdyby wersja rozwiązania Tima, która działa lepiej na większą skalę. Proszę zmierzyć wydajność tego rozwiązania z "dużym dokumentem XML" jako danymi wejściowymi.

To XSLT 1.0 style-sheet ...

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

<xsl:key name="kTypes" match="Type" use="." /> 
<xsl:variable name="distinct-types" 
    select="/*/Person/FamilyMembers/FamilyMember/Type[ 
    generate-id()=generate-id(key('kTypes',.)[1])]" /> 

<xsl:template match="/"> 
    <xsl:value-of select="'Name;'" /> 
    <xsl:for-each select="$distinct-types"> 
    <xsl:value-of select="." /> 
    <xsl:if test="position() &lt; last()"> 
     <xsl:value-of select="';'" /> 
    </xsl:if> 
    </xsl:for-each> 
    <xsl:value-of select="'&#x0A;'" /> 
    <xsl:apply-templates select="*/Person" /> 
</xsl:template> 

<xsl:template match="Person"> 
    <xsl:value-of select="concat(Name,';')" /> 
    <xsl:variable name="family" select="FamilyMembers/FamilyMember" /> 
    <xsl:for-each select="$distinct-types"> 
    <xsl:variable name="type" select="string(.)" /> 
    <xsl:value-of select="$family/self::*[Type=$type]/Name" /> 
    <xsl:if test="position() &lt; last()"> 
     <xsl:value-of select="';'" /> 
    </xsl:if> 
    </xsl:for-each> 
    <xsl:value-of select="'&#x0A;'" /> 
</xsl:template> 

</xsl:stylesheet> 

... zmieni to wejście (lub innym efektywnie w skali) ...

<t> 
<Person> 
    <Name>John</Name> 
    <FamilyMembers> 
     <FamilyMember> 
      <Name>Lisa</Name> 
      <Type>Sister</Type> 
     </FamilyMember> 
     <FamilyMember> 
      <Name>Tom</Name> 
      <Type>Brother</Type> 
     </FamilyMember> 
    </FamilyMembers> 
</Person> 
<Person> 
    <Name>Daniel</Name> 
    <FamilyMembers> 
     <FamilyMember> 
      <Name>Peter</Name> 
      <Type>Father</Type> 
     </FamilyMember> 
    </FamilyMembers> 
</Person> 
</t> 

. .. i wypisz tekst ...

Name;Sister;Brother;Father 
John;Lisa;Tom; 
Daniel;;;Peter 
+0

Zrobię to przetestować i wyrazić opinię. Może zająć trochę czasu. – Andre