2010-11-17 28 views
8

không chắc chắn nếu điều này là có thể mà không phải trải qua nhiều lần, nhưng tôi sẽ hỏi (XSL của tôi hơi bị gỉ)xslt để bỏ qua các nút "đã truy cập"

Tôi có tài liệu XML chứa các nút như sau: (! các tập tin thực chứa rất nhiều thẻ cấu trúc mà phụ thuộc lẫn nhau, không ai trong số đó là hình tròn)

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

Những gì tôi muốn làm là để tạo ra một số văn bản (trong trường hợp này C++ struct s) và yêu cầu hiển nhiên là thứ tự o f các struct s, vì vậy sản lượng lý tưởng của tôi sẽ là

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

Tôi biết tôi có thể sử dụng tờ khai phía trước và điều đó có nghĩa rằng thứ tự không quan trọng, tuy nhiên vấn đề là sẽ có "xử lý" mã inline trong các cấu trúc, và chúng sẽ yêu cầu có định nghĩa thực sự.

Cho đến nay tôi có thể phát hiện để xem nếu một structure có bất kỳ phụ thuộc, với các bit sau xsl:

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

(điều này xảy ra bên trong một <xsl:template match="structure">)

Bây giờ, về mặt lý thuyết, tôi có thể sau đó hãy làm theo chuỗi "phụ thuộc" này và tạo ra struct s cho mỗi mục nhập trước tiên, sau đó bạn có thể tưởng tượng, điều này tạo ra nhiều bản sao của cùng một cấu trúc - đó là một nỗi đau ..

Có cách nào để tránh các bản sao? Về cơ bản, khi một cấu trúc đã được truy cập, và nếu chúng ta ghé thăm lần nữa, đừng bận tâm xuất mã cho nó ... Tôi không cần xslt đầy đủ để làm điều này (trừ khi nó tầm thường!), Nhưng chỉ bất kỳ ý tưởng nào về cách tiếp cận ...

Nếu không có, tôi có thể về mặt lý thuyết quấn struct với một người bảo vệ #ifdef/#define/#endif để trình biên dịch chỉ sử dụng định nghĩa đầu tiên, tuy nhiên điều này là thực sự khó chịu! :(

(GHI CHÚ: XSLT 1.0, xsltproc trên linux: Sử dụng libxml 20.623, libxslt 10115 và libexslt 812)

+0

xuất sắc câu hỏi, 1. Xem câu trả lời của tôi cho một giải pháp hoàn chỉnh và ngắn. :) –

Trả lời

7

này chuyển đổi:

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

khi áp dụng trên tài liệu XML cung cấp:

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

tạo ra truy nã, chính xác kết quả:

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

Giải thích: structure yếu tố được xử lý đúng từng cái một. Bất cứ lúc nào chúng tôi xử lý phần tử structure đầu tiên có số id chưa được đăng ký trong thông số pVisited và không có giá trị field/@idref chưa có trong thông số pVisited và đề cập đến phần tử structure hiện tại.

+0

+1 cách tiếp cận tốt: quá trình từ các lá lên, để bạn không phải sửa đổi và vượt qua càng nhiều thông tin tiểu bang. Tôi vẫn đang tiêu hóa câu trả lời của bạn ... – LarsH

+0

+1, hoạt động cho tôi, bây giờ cần phải ánh xạ nó với tài liệu xml thực mà tôi có, điều đó sẽ rất thú vị! Cảm ơn v. Nhiều gents! – Nim

+0

@Dimitre: +1 Câu trả lời rất hay! –

2

Ooh, đây là phức tạp hơn nó nhìn đầu tiên 1 cho câu hỏi hay

..

Tôi nghĩ cách tốt nhất để thực hiện điều này trong XSLT 1.0 là chuyển một tham số tích lũy bất cứ khi nào bạn áp dụng các mẫu cho một cấu trúc. Tham số (gọi là "$ visited-structure") là danh sách các tên được phân cách bằng dấu cách các cấu trúc bạn đã xử lý.

Cập nhật: cuối cùng cũng có điều này. :-)

Trong mẫu để xử lý cấu trúc, hãy kiểm tra xem có cấu trúc nào khác mà cấu trúc này phụ thuộc vào chưa được liệt kê trong cấu trúc $ truy cập không. Nếu không, hãy tạo mã cho cấu trúc này và recurse trên mẫu chọn cấu trúc không truy cập tiếp theo, gắn thêm tên cấu trúc hiện tại vào tham số $ visited-structure. Nếu không, không tạo mã cho cấu trúc nhưng recurse trên mẫu chọn cấu trúc phụ thuộc đầu tiên, chuyển tham số $ visited-structure chưa sửa đổi.

Đây là mã ...

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

Và kết quả:

<?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. Bạn cũng phải chuyển một tham số bổ sung vào mẫu cho '', cho biết có hay không tái xử lý đối với anh chị em sau đây. Điều này sẽ chỉ đúng ở cấp cao nhất, tức là khi được áp dụng trong mẫu để xử lý ''. – LarsH

+0

biến được truyền âm thanh như ý tưởng, tuy nhiên sẽ không có vấn đề về phạm vi? tức là mỗi nút 'cấu trúc' gốc được gọi với một' $ truy cập-cấu trúc 'trống? Do đó sẽ vẫn có nhiều bản sao của các nút cấu trúc? – Nim

+0

Ồ, có vẻ như bạn thậm chí phải vượt qua "sự tiếp tục"! Hoặc ít nhất một danh sách các cấu trúc chưa được xử lý. – LarsH

3

Just for fun, cách tiếp cận khác (cấp theo trình độ) và ussing phím:

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

Output:

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

Tốt. Tôi không chắc rằng tôi đã từng thấy mẫu "/" áp dụng mẫu "/" đệ quy. :-) – LarsH

+0

@LarsH: Ja! Điều đó tương tự như việc sử dụng các tham chiếu mẫu. Nhưng trong trường hợp này chỉ là một dạng bí truyền để tránh một 'xsl: if' ... –

Các vấn đề liên quan