2009-03-05 37 views
52

Tôi có một tài liệu XML và tôi muốn thay đổi các giá trị cho một trong các thuộc tính.XSLT: Cách thay đổi giá trị thuộc tính trong thời gian <xsl:copy>?

Trước tiên tôi sao chép tất cả mọi thứ từ đầu vào đến đầu ra sử dụng:

<xsl:template match="@*|node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

Và bây giờ tôi muốn thay đổi giá trị của thuộc tính "type" trong bất kỳ yếu tố tên "property".

+0

Đối với những người muốn có một giải pháp chung: một số giá trị mới đây astonia

+2

Giải pháp của bạn là không cần thiết và tiết lộ một phần sai. Nên có ''http: // www.'' ở đầu của không gian tên' xsl'. Ngoài ra, việc kết hợp/chọn 'node() | comment() | processing-instruction() | text()' là thừa, vì các chú thích, các lệnh xử lý và các nút văn bản được kết hợp bởi 'node()'. – Flynn1179

+0

@ Flynn1179 Giải pháp của tôi hoạt động tốt cho mọi tình huống. Tôi không biết tại sao http: // bị thiếu sau khi sao chép/dán, đó là một sai lầm, cảm ơn bạn đã chỉ ra. Tôi chỉ đưa ra một giải pháp có thể, không phải là giải pháp hoàn hảo. Điều quan trọng nhất là giải pháp của tôi làm việc cho hầu như tất cả các tình huống mặc dù "nó không cần thiết" như bạn đã nói. Mặt khác, hầu hết các câu trả lời khác bao gồm câu trả lời "chuyên gia xslt" đã không làm việc gì cả. Nhưng họ không thừa nhận điều đó. – astonia

Trả lời

33

Thử nghiệm trên một ví dụ đơn giản, hoạt động tốt:

<xsl:template match="@*|node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 
<xsl:template match="@type[parent::property]"> 
    <xsl:attribute name="type"> 
    <xsl:value-of select="'your value here'"/> 
    </xsl:attribute> 
</xsl:template> 

Edited để bao gồm đề nghị Tomalak của.

+1

Phiên bản thay thế sẽ là Tomalak

+0

Đồng ý. Cách của bạn có lẽ trực quan hơn vì nó khớp với cách hợp lý hơn với những gì mà khuôn mẫu dành cho. – Welbog

+1

Đó là những gì tôi muốn nói trong bình luận ban đầu, nhưng quên thực sự gõ nó. ;-) – Tomalak

4

Bạn cần mẫu sẽ khớp với thuộc tính đích của bạn và không có gì khác.

<xsl:template match='XPath/@myAttr'> 
    <xsl:attribute name='myAttr'>This is the value</xsl:attribute> 
</xsl:template> 

Đây là phần bổ sung cho "sao chép tất cả" bạn đã có (và thực sự luôn có mặt theo mặc định trong XSLT). Có một trận đấu cụ thể hơn, nó sẽ được sử dụng theo sở thích.

+0

Tôi đã thử nó mà không có phần "sao chép tất cả" và nó chỉ có những gì đã được giữa các thẻ. Không có thẻ nào của chính chúng hoặc các thuộc tính được sao chép. – tomato

+0

@coderx: Cần xem mẫu, không chắc chắn ý bạn là gì. – Richard

1

Đối với XML sau:

<?xml version="1.0" encoding="utf-8"?> 
<root> 
    <property type="foo"/> 
    <node id="1"/> 
    <property type="bar"> 
     <sub-property/> 
    </property> 
</root> 

tôi đã có thể có được nó để làm việc với XSLT sau:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="//property"> 
     <xsl:copy> 
      <xsl:attribute name="type"> 
       <xsl:value-of select="@type"/> 
       <xsl:text>-added</xsl:text> 
      </xsl:attribute> 
      <xsl:copy-of select="child::*"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
59

Vấn đề này có một giải pháp cổ điển: Sử dụng và trọng the identity template là một trong những mẫu thiết kế XSLT cơ bản và mạnh mẽ nhất:

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

    <xsl:param name="pNewType" select="'myNewType'"/> 

    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="property/@type"> 
     <xsl:attribute name="type"> 
      <xsl:value-of select="$pNewType"/> 
     </xsl:attribute> 
    </xsl:template> 
</xsl:stylesheet> 

Khi áp dụng trên tài liệu này XML:

<t> 
    <property>value1</property> 
    <property type="old">value2</property> 
</t> 

kết quả mong muốn được sản xuất:

<t> 
    <property>value1</property> 
    <property type="myNewType">value2</property> 
</t> 
+1

Giải pháp này không hoạt động nếu có định nghĩa vùng tên. Tôi đã viết bình luận một vài ngày trước, và nhà văn của câu trả lời trả lời. Nhưng bây giờ họ đã biến mất, vì vậy tôi phải viết lại nhận xét cho những người đến đây không bị sai lầm bởi những câu trả lời sai, đặc biệt là bởi những nhà văn có khuynh hướng sai lầm. – astonia

+0

Có lẽ bạn đang quá tập trung vào lý thuyết thay vì vấn đề chính nó. Google đưa tôi đến đây, câu trả lời của bạn rất hữu ích, nhưng không thể giải quyết được vấn đề của tôi chút nào. Vì vậy, tôi cuối cùng đã có một cái tốt hơn bất cứ điều gì nó về mặt lý thuyết đúng hay sai, hoặc có thể gây ra một số điên về không gian tên một cái gì đó. Những gì tôi quan tâm là tìm cách giải quyết vấn đề của mình và tôi hy vọng kinh nghiệm của tôi có thể giúp những người khác có những tình huống tương tự. Câu trả lời của bạn thực sự hữu ích và bạn thực sự là một người trả lời nhiệt tình ở đây. Nhưng tôi phải nói, giải pháp bạn đưa ra cho câu hỏi này không có tác dụng gì cả. – astonia

+0

Giải pháp này không hoạt động đối với tôi nếu có định nghĩa vùng tên trên phần tử gốc. – dps

2

Tôi đã có một trường hợp tương tự mà tôi muốn xóa một thuộc tính từ một đơn giản và không thể tìm ra trục nào sẽ cho phép tôi đọc tên thuộc tính. Cuối cùng, tất cả tôi phải làm là sử dụng

@*[name(.)!='AttributeNameToDelete']

+1

+1 vì cấu trúc này hữu ích nếu một người muốn thay đổi thuộc tính trong một bản sao. nhưng câu trả lời chưa hoàn chỉnh. Xem câu trả lời này cho những gì tôi có nghĩa là: http://stackoverflow.com/a/12919373/520567 – akostadinov

5

Hai câu trả lời trên sẽ không làm việc nếu có một định nghĩa xmlns trong phần tử gốc:

<?xml version="1.0"?> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
    <property type="old"/> 
</html> 

Tất cả các giải pháp sẽ không hoạt động cho xml ở trên.

Các giải pháp khả thi là như:

<?xml version="1.0"?> 

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

    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:template match="node()[local-name()='property']/@*[local-name()='type']"> 
     <xsl:attribute name="{name()}" namespace="{namespace-uri()}"> 
       some new value here 
      </xsl:attribute> 
    </xsl:template> 

    <xsl:template match="@*|node()|comment()|processing-instruction()|text()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
+0

Bạn đang làm cho điều này phức tạp hơn nhiều so với nó cần phải được. Tôi đã đăng câu trả lời cho thấy cách làm cho hai câu trả lời hàng đầu này hoạt động trong tình huống của bạn. –

+0

Câu trả lời của bạn phức tạp hơn tôi nhiều. Tôi không thể thấy lý do tại sao bạn trả lời thêm sau bài đăng của tôi. Những gì bạn nên làm là cộng với câu trả lời của tôi. Và thành thật mà nói, câu trả lời của bạn là sai nếu thuộc tính có một không gian tên quá. – astonia

1

Nếu nguồn tài liệu XML của bạn có không gian tên riêng của mình, bạn cần phải khai báo không gian tên trong stylesheet của bạn, gán cho nó một tiền tố, và sử dụng tiền tố rằng khi đề cập đến các yếu tố của nguồn XML - ví dụ:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xhtml="http://www.w3.org/1999/xhtml"> 

<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" /> 

<!-- identity transform --> 
<xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<!-- exception-->  
<xsl:template match="xhtml:property/@type"> 
    <xsl:attribute name="type"> 
     <xsl:text>some new value</xsl:text> 
    </xsl:attribute> 
</xsl:template> 

</xsl:stylesheet> 

Hoặc, nếu bạn thích:

... 
<!-- exception-->  
<xsl:template match="@type[parent::xhtml:property]"> 
    <xsl:attribute name="type"> 
     <xsl:text>some new value</xsl:text> 
    </xsl:attribute> 
</xsl:template> 
... 

PHỤ LỤC: Trong trường hợp rất khó nơi không gian tên XML không biết trước, bạn có thể làm:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" /> 

<!-- identity transform --> 
<xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<!-- exception --> 
<xsl:template match="*[local-name()='property']/@type"> 
    <xsl:attribute name="type"> 
     <xsl:text>some new value</xsl:text> 
    </xsl:attribute> 
</xsl:template> 

Tất nhiên, nó rất khó để tưởng tượng một kịch bản mà bạn sẽ biết trước rằng tài liệu XML nguồn có chứa một phần tử có tên là "property", với một thuộc tính có tên là "type" cần thay thế - nhưng vẫn không biết namespace của tài liệu. Tôi đã thêm chủ yếu này để hiển thị cách giải pháp của riêng bạn có thể được sắp xếp hợp lý.

+0

Kịch bản không gian tên không xác định không có khả năng xảy ra. Ít nhất bạn có thể viết một xslt để xử lý tất cả xml bất kể không gian tên của chúng là gì. Ví dụ, tôi cần phải biến đổi thuộc tính src của thành một bức tranh trống cho các trang của hàng ngàn trang web được thu thập dữ liệu từ internet. Rõ ràng, định nghĩa không gian tên của chúng chưa được xác định. Và mỗi khi bạn tham gia một dự án mới nếu cần xslt, mẫu chung có thể là một trong những bộ công cụ cơ sở của bạn. Bạn không phải thay đổi không gian tên cho các dự án khác nhau. – astonia

+0

Và câu trả lời của bạn là sai nếu thuộc tính có không gian tên quá. Tôi không biết tại sao bạn đưa ra một câu trả lời sai sau bài viết của tôi. – astonia

0

Tôi cũng đã xem qua cùng một vấn đề và tôi giải quyết nó như sau:

<!-- identity transform --> 
<xsl:template match="@*|node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

<!-- copy property element while only changing its type attribute --> 
<xsl:template match="property"> 
    <xsl:copy> 
    <xsl:attribute name="type"> 
     <xsl:value-of select="'your value here'"/> 
    </xsl:attribute> 
    <xsl:apply-templates select="@*[not(local-name()='type')]|node()"/> 
    <xsl:copy> 
</xsl:template> 
Các vấn đề liên quan