2012-11-20 15 views
5

in questa discussione faccio riferimento alla mia ultima discussione Convert XML to CSV using XSLT - dynamic columns.XSLT per generare un altro script XSLT

Lo script XSLT nel thread indicato funziona bene ma con un documento xml di grandi dimensioni le prestazioni non sono buone. Ora voglio scrivere uno script XSLT che emetta un altro script XSLT che emetterà il file CSV finale.

La mia domanda ora è, come scrivere il primo script XSLT. L'output dovrebbe essere simile al seguente:

<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> 

Il file di input XML è la stessa come nel thread refered:

<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> 

Quindi per ogni diverso tipo di elemento dovrebbe esserci una riga come:

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

nello script XSLT risultante.

Forse alcuni di voi hanno un buon consiglio per me. Grazie in anticipo!

André

+0

In realtà non occorre una soluzione a due esterni fasi. Tutto quello che devi fare è migliorare l'efficienza su larga scala della soluzione di Tim. La vera domanda che dovresti porci è come ottimizzare su larga scala. –

risposta

11

Per scrivere un XSLT che fornisce in uscita un altro vi sia bisogno di generare gli elementi d'uscita utilizzando <xsl:element>, ad esempio

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

o utilizzare <xsl:namespace-alias> se si desidera utilizzare elementi di risultato letterali. Il XSLT spec ha un esempio:

<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> 

Eventuali <axsl:...> elementi nel foglio di stile risulteranno <xsl:...> nell'output.

+0

La tua soluzione si adatta meglio alla mia domanda. L'uso di axsl è il mio modo attuale. Metterò alla prova la soluzione di Sean più tardi. – Andre

1

Piuttosto che una soluzione a due fasi esterne (ovvero un foglio di stile che scrive un foglio di stile che viene eseguito), penso che sarebbe meglio servirsi di una versione della soluzione di Tim che offre prestazioni migliori. Si prega di misurare le prestazioni di questa soluzione con il vostro 'documento XML di grandi dimensioni' come input.

Questo XSLT 1.0 foglio di stile ...

<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> 

... trasformerà questo ingresso (o gli altri in modo efficiente a scala) ...

<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> 

. .. e fornire testo ...

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

Lo farò testare e dare un feedback. Potrebbe volerci del tempo – Andre