2010-11-17 21 views
8

emin değilim zaten "ziyaret" düğümleri atlamak için, ama yine isteyeceğizxslt bu birkaç geçiş gitmek zorunda kalmadan mümkün olup olmadığını

Ben bir XML belgesi var (benim XSL biraz paslanmış) hangi aşağıdaki gibi düğümleri içerir: (! gerçek dosya bağımlılıklar, hiçbiri dairesel olan yapı etiketleri bolca içerir)

<structures> 
<structure id="STRUCT_A"> 
    <field idref="STRUCT_B" name="b"/> 
    <field idref="STRUCT_C" name="c"/> 
    <field idref="FIELD_D" name="d"/> 
</structure> 

<structure id="STRUCT_B"> 
    <field idref="STRUCT_C" name="c"/> 
    <field idref="FIELD_E" name="e"/> 
</structure> 

<structure id="STRUCT_C"> 
    <field idref="FIELD_E" name="e"/> 
    <field idref="FIELD_F" name="f"/> 
    <field idref="FIELD_G" name="g"/> 
</structure> 
</structures> 

ne yapmak istiyorum bu durumda C bazı metin (üretmektir ++ struct s) ve açık gereklilik o sipariş struct s f, bu nedenle benim ideal çıkış Ben bildirimleri ileri kullanabilirsiniz biliyor ve o sırası önemli olmadığı anlamına gelecektir

struct STRUCT_C 
{ 
    FIELD_E e; 
    FIELD_F f; 
    FIELD_G g; 
}; 

struct STRUCT_B 
{ 
    STRUCT_C c; 
    FIELD_E e; 
}; 

struct STRUCT_A 
{ 
    STRUCT_B b; 
    STRUCT_C c; 
    FIELD_D d; 
}; 

olurdu, ancak sorun "işleme" kod satır içi olacağı ise yapılarda ve gerçek tanımın mevcut olmasını gerektirirler.

<xsl:for-each select="descendant::*/@idref"> 
    <xsl:variable name="name" select="."/> 
    <xsl:apply-templates select="//structure[@id = $name]" mode="struct.dep"/> 
</xsl:for-each> 

Sonra olabilir, teorik olarak, Şimdi

(bu <xsl:template match="structure"> içeride olur): Şimdiye kadar ben structure xsl aşağıdaki bit ile herhangi bir bağımlılık olup olmadığını görmek için algılayabilir

Bu bağımlılığı "zincir" izleyin ve her bir giriş için önce struct s, sonra şu anda olduğum gibi, ancak hayal edebileceğiniz gibi, bu aynı yapının çok sayıda kopyasını üretir - bu bir acıdır ..

Kopyalardan kaçınmak için zaten var mı? Temel olarak, bir yapı ziyaret edildiğinde ve tekrar ziyaret edersek, kodun çıktısını almamaya özen göstermeliyiz ... Bunu yapmak için tam xslt'e ihtiyacım yok (önemsiz olmadıkça!), Fakat sadece yaklaşımlarla ilgili herhangi bir fikir ...

yoksa derleyici yalnızca ilk tanımını kullandığını, ancak bu gerçekten kötü olduğunu bu yüzden, ben teoride bir #ifdef/#define/#endif görevlisi ile struct sarabilirdiniz! :(

(NOTLAR: xslt 1.0, linux üzerinde Xsltproc: kullanma libxml 20623, Libxslt'nin 10115 ve libexslt 812)

+0

Mükemmel bir soru, 1. Tam ve kısa bir çözüm için cevabımı görün. :) –

cevap

7

Bu dönüşüm:

<structures> 
<structure id="STRUCT_A"> 
    <field idref="STRUCT_B" name="b"/> 
    <field idref="STRUCT_C" name="c"/> 
    <field idref="FIELD_D" name="d"/> 
</structure> 

<structure id="STRUCT_B"> 
    <field idref="STRUCT_C" name="c"/> 
    <field idref="FIELD_E" name="e"/> 
</structure> 

<structure id="STRUCT_C"> 
    <field idref="FIELD_E" name="e"/> 
    <field idref="FIELD_F" name="f"/> 
    <field idref="FIELD_G" name="g"/> 
</structure> 
</structures> 
: sağlanan XML belgesinin uygulanan

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

<xsl:variable name="vLeafs" select="/*/structure[not(field/@idref = /*/structure/@id)]"/> 

<xsl:template match="/*"> 
    <xsl:apply-templates select="$vLeafs[1]"> 
    <xsl:with-param name="pVisited" select="'|'"/> 
    </xsl:apply-templates> 

</xsl:template> 

<xsl:template match="structure"> 
    <xsl:param name="pVisited"/> 

struct <xsl:value-of select="@id"/> 
{<xsl:text/> 
    <xsl:apply-templates/> 
}; 
    <xsl:variable name="vnewVisited" 
     select="concat($pVisited, @id, '|')"/> 
    <xsl:apply-templates select= 
    "../structure[not(contains($vnewVisited, concat('|', @id, '|'))) 
       and 
       not(field/@idref 
          [not(contains($vnewVisited, concat('|', ., '|'))) 
          and 
          . = ../../../structure/@id 
          ] 
        ) 
       ] [1] 
    "> 
    <xsl:with-param name="pVisited" select="$vnewVisited"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="field"> 
    <xsl:value-of select="concat('&#xA; ', @idref, ' ', @name, ';')"/> 
</xsl:template> 
</xsl:stylesheet> 

struct STRUCT_C 
{ 
    FIELD_E e; 
    FIELD_F f; 
    FIELD_G g; 
}; 


struct STRUCT_B 
{ 
    STRUCT_C c; 
    FIELD_E e; 
}; 


struct STRUCT_A 
{ 
    STRUCT_B b; 
    STRUCT_C c; 
    FIELD_D d; 
}; 

Açıklama: structure elemanları teker kesinlikle tek işlenir

aranan, doğru sonucu üretir. Herhangi bir zamanda, idpVisited parametresinde henüz kayıtlı olmayan ve pVisited parametresinde olmayan ve mevcut bir structure öğesine başvuran structure öğesini işlerken ilk structure öğesini işliyoruz.

+0

+1 iyi bir yaklaşım: yaprakları yukarıdan işler, böylece durum bilgisini değiştirmek ve iletmek zorunda kalmazsınız. Cevabını hala sindiriyorum ... – LarsH

+0

+1, benim için çalışıyor, şimdi sahip olduğum gerçek xml belgesine eşleştirmeliyim, eğlenceli olacak! Teşekkürler v. Çok beyler! – Nim

+0

@Dimitre: +1 Çok iyi cevap! –

2

Ooh, bu iyi bir soru için ilk +1 baktı daha karmaşıktır

..

XSLT 1.0 uygulamasında bunu yapmanın en iyi yolu, bir yapıya şablonlar uyguladığınızda biriken bir parametreyi aktarmak olacaktır.O parametresi ("ziyaret edilen" $ ziyaret edilen yapılar "), alanın sınırlandırılmış listesidir. işlemiş olduğunuz yapılar

Güncelleme: sonunda şunu elde ettim :-) Bir yapıyı işlemek için şablonda, bu yapının bağlı olduğu diğer yapıların zaten ziyaret edilen $ yapıları listesinde bulunup bulunmadığını kontrol edin. Değilse, bu yapı için kod oluşturun ve mevcut yapı adını $ ziyaret edilen yapıya parametrelere ekleyerek, bir sonraki ziyaret edilmeyen yapıyı seçerek tekrarlayın. Aksi halde, yapı için kod üretmeyin, ancak değiştirilmemiş $ ziyaret edilen yapıları geçirerek ilk bağımlılık yapısını seçerek şablonda tekrarlayın.

İşte kod ...

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0"> 
    <xsl:key name="structuresById" match="/*/structure" use="@id" /> 

    <xsl:template match="structures"> 
     <xsl:apply-templates select="structure[1]" > 
     <!-- a space-delimited list of id's of structures already processed, with space 
      at beginning and end. Could contain duplicates. --> 
     <xsl:with-param name="visited-structures" select="' '"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="structure"> 
     <xsl:param name="visited-structures" select="' '" /> 
     <xsl:variable name="dependencies" select="key('structuresById', field/@idref) 
        [not(contains($visited-structures, @id))]"/> 
     <xsl:choose> 
     <xsl:when test="$dependencies"> 
      <xsl:apply-templates select="$dependencies[1]"> 
       <xsl:with-param name="visited-structures" select="$visited-structures"/> 
      </xsl:apply-templates>    
     </xsl:when> 
     <xsl:otherwise> 
      <!-- Now generate code for this structure ... ... --> 
struct <xsl:value-of select="@id"/> 
{ 
<xsl:apply-templates select="field"/>}; 
      <xsl:variable name="new-visited" select="concat(' ', @id, $visited-structures)"/> 
      <xsl:apply-templates select="/*/structure[not(contains($new-visited, @id))][1]" > 
       <xsl:with-param name="visited-structures" select="$new-visited"/> 
      </xsl:apply-templates> 
     </xsl:otherwise> 
     </xsl:choose>  
    </xsl:template> 

    <xsl:template match="field"> 
     <xsl:value-of select="concat(' ', @idref, ' ', @name, ';&#xa;')"/>  
    </xsl:template> 

</xsl:stylesheet> 

Ve çıkışı:

<?xml version="1.0" encoding="utf-8"?> 

struct STRUCT_C 
{ 
    FIELD_E e; 
    FIELD_F f; 
    FIELD_G g; 
}; 


struct STRUCT_B 
{ 
    STRUCT_C c; 
    FIELD_E e; 
}; 


struct STRUCT_A 
{ 
    STRUCT_B b; 
    STRUCT_C c; 
    FIELD_D d; 
}; 
+0

P.S. Ayrıca, 'için şablona ek bir parametre göndermeniz ve aşağıdaki kardeşte tekrarlanıp tekrarlanmayacağını belirtmeniz gerekir. Bu, yalnızca 'işlemek için şablonda uygulandığında en üst düzeyde doğru olabilir. – LarsH

+0

değişkeni, bir fikir gibi geçiyor, ancak bir kapsam sorunu olmayacak mı? diğer bir deyişle, her kökü 'yapı 'düğümü boş' $ ziyaret edilen yapılar 'ile çağrılır? Bu nedenle, yapı düğümlerinin birden çok kopyası var mı? – Nim

+0

Vay, bir "devam" dan geçmek zorunda gibi görünüyor! Ya da en azından işlemek için henüz bir yapı listesi. – LarsH

3

Sadece eğlence, diğer yaklaşım (düzeyine göre seviye) ve Ussing anahtarlar için:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:key name="kStructById" match="structure" use="@id"/> 
    <xsl:key name="kStructByIdref" match="structure" use="field/@idref"/> 
    <xsl:template match="/"> 
     <xsl:param name="pParents" select="/.."/> 
     <xsl:param name="pChilds" 
        select="structures/structure[not(key('kStructById', 
                 field/@idref))]"/> 
     <xsl:variable name="vParents" select="$pParents|$pChilds"/> 
     <xsl:variable name="vChilds" 
         select="key('kStructByIdref',$pChilds/@id) 
           [count(key('kStructById', 
              field/@idref) | 
             $vParents) = 
            count($vParents)]"/> 
     <xsl:apply-templates select="$pChilds"/> 
     <xsl:apply-templates select="current()[$vChilds]"> 
      <xsl:with-param name="pParents" select="$vParents"/> 
      <xsl:with-param name="pChilds" select="$vChilds"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="structure"> 
     <xsl:value-of select="concat('struct ',@id,'&#xA;{&#xA;')"/> 
     <xsl:apply-templates/> 
     <xsl:text>};&#xA;</xsl:text> 
    </xsl:template> 
    <xsl:template match="field"> 
     <xsl:value-of select="concat('&#x9;',@idref,' ',@name,';&#xA;')"/> 
    </xsl:template> 
</xsl:stylesheet> 

Çıktı:

struct STRUCT_C 
{ 
    FIELD_E e; 
    FIELD_F f; 
    FIELD_G g; 
}; 
struct STRUCT_B 
{ 
    STRUCT_C c; 
    FIELD_E e; 
}; 
struct STRUCT_A 
{ 
    STRUCT_B b; 
    STRUCT_C c; 
    FIELD_D d; 
}; 
+0

Güzel. "/" Şablonunu şablonlarını "/" yinelemeli olarak uyguladığımdan emin değilim. :-) – LarsH

+0

@LarsH: Ja! Bu şablon referanslarını kullanmakla aynı şey. Ama bu durumda sadece bir 'xsl: if' önlemek için ezoterik bir form ... –

İlgili konular