2009-07-21 64 views
21

Làm cách nào để chuyển đổi XML sau thành văn bản đã thoát bằng XSLT?Chuyển đổi XML sang văn bản đã thoát trong XSLT

Nguồn:

<?xml version="1.0" encoding="utf-8"?> 
<abc> 
    <def ghi="jkl"> 
    mnop 
    </def> 
</abc> 

Output:

<TestElement>&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;abc&gt;&lt;def ghi="jkl"&gt; 
    mnop 
    &lt;/def&gt;&lt;/abc&gt;</TestElement> 

Hiện nay, tôi đang cố gắng XSLT sau đây và nó dường như không hoạt động đúng:

<?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" /> 
    <xsl:template match="/"> 
    <xsl:variable name="testVar"> 
     <xsl:copy> 
     <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:variable> 

    <TestElement> 
     <xsl:value-of select="$testVar"/> 
    </TestElement> 
    </xsl:template> 
</xsl:stylesheet> 

Sản lượng Câu lệnh XSLT của .NET XslCompiledTransform xuất hiện như sau:

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

    mnop 

</TestElement> 
+1

Chính xác nó hoạt động như thế nào? –

+0

Tôi đã thêm đầu ra của XSLT theo nhận xét của John. –

Trả lời

33

Mã của bạn hoạt động theo cách đó vì xsl:value-of truy xuất string-value của tập hợp nút.

Để làm những gì bạn muốn, tôi sợ rằng bạn sẽ phải mã nó một cách rõ ràng:

<xsl:template match="/"> 
     <TestElement> 
      <xsl:apply-templates mode="escape"/> 
     </TestElement> 
    </xsl:template> 

    <xsl:template match="*" mode="escape"> 
     <!-- Begin opening tag --> 
     <xsl:text>&lt;</xsl:text> 
     <xsl:value-of select="name()"/> 

     <!-- Namespaces --> 
     <xsl:for-each select="namespace::*"> 
      <xsl:text> xmlns</xsl:text> 
      <xsl:if test="name() != ''"> 
       <xsl:text>:</xsl:text> 
       <xsl:value-of select="name()"/> 
      </xsl:if> 
      <xsl:text>='</xsl:text> 
      <xsl:call-template name="escape-xml"> 
       <xsl:with-param name="text" select="."/> 
      </xsl:call-template> 
      <xsl:text>'</xsl:text> 
     </xsl:for-each> 

     <!-- Attributes --> 
     <xsl:for-each select="@*"> 
      <xsl:text> </xsl:text> 
      <xsl:value-of select="name()"/> 
      <xsl:text>='</xsl:text> 
      <xsl:call-template name="escape-xml"> 
       <xsl:with-param name="text" select="."/> 
      </xsl:call-template> 
      <xsl:text>'</xsl:text> 
     </xsl:for-each> 

     <!-- End opening tag --> 
     <xsl:text>&gt;</xsl:text> 

     <!-- Content (child elements, text nodes, and PIs) --> 
     <xsl:apply-templates select="node()" mode="escape" /> 

     <!-- Closing tag --> 
     <xsl:text>&lt;/</xsl:text> 
     <xsl:value-of select="name()"/> 
     <xsl:text>&gt;</xsl:text> 
    </xsl:template> 

    <xsl:template match="text()" mode="escape"> 
     <xsl:call-template name="escape-xml"> 
      <xsl:with-param name="text" select="."/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template match="processing-instruction()" mode="escape"> 
     <xsl:text>&lt;?</xsl:text> 
     <xsl:value-of select="name()"/> 
     <xsl:text> </xsl:text> 
     <xsl:call-template name="escape-xml"> 
      <xsl:with-param name="text" select="."/> 
     </xsl:call-template> 
     <xsl:text>?&gt;</xsl:text> 
    </xsl:template> 

    <xsl:template name="escape-xml"> 
     <xsl:param name="text"/> 
     <xsl:if test="$text != ''"> 
      <xsl:variable name="head" select="substring($text, 1, 1)"/> 
      <xsl:variable name="tail" select="substring($text, 2)"/> 
      <xsl:choose> 
       <xsl:when test="$head = '&amp;'">&amp;amp;</xsl:when> 
       <xsl:when test="$head = '&lt;'">&amp;lt;</xsl:when> 
       <xsl:when test="$head = '&gt;'">&amp;gt;</xsl:when> 
       <xsl:when test="$head = '&quot;'">&amp;quot;</xsl:when> 
       <xsl:when test="$head = &quot;&apos;&quot;">&amp;apos;</xsl:when> 
       <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise> 
      </xsl:choose> 
      <xsl:call-template name="escape-xml"> 
       <xsl:with-param name="text" select="$tail"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 

Lưu ý rằng giải pháp này bỏ qua comment nút, và chèn các nút namespace không cần thiết (như namespace:: trục sẽ bao gồm tất cả các nút được thừa kế từ cấp độ gốc). Tuy nhiên, về các không gian tên, kết quả XML được trích dẫn sẽ tương đương về mặt ngữ nghĩa với ví dụ mà bạn đã cung cấp trong thư trả lời của bạn (vì các phép lặp lại đó không thực sự thay đổi bất kỳ thứ gì).

Ngoài ra, điều này sẽ không thoát khỏi tuyên bố <?xml ... ?>, đơn giản vì nó không có trong mô hình dữ liệu XPath 1.0 (nó không phải là hướng dẫn xử lý). Nếu bạn thực sự cần nó trong đầu ra, bạn sẽ phải chèn nó bằng tay (và chắc chắn rằng mã hóa nó chỉ định là phù hợp với mã hóa tuần tự hóa của bộ xử lý XSLT của bạn).

+0

tuyệt vời !!!!! +1 –

-1

Tại sao không thể bạn chỉ cần chạy

<xsl:template match="/"> 
    <TestElement> 
    <xsl:copy-of select="." /> 
    </TestElement> 
</xsl:template> 
+2

Bởi vì nó sẽ tạo ra một cây XML, không phải là chuỗi đại diện cho cây XML (như OP được yêu cầu). – bortzmeyer

0

Bạn cần sử dụng XSLT? Bởi vì, vì lý do được giải thích bởi Pavel Minaev, nó sẽ đơn giản hơn nhiều khi sử dụng một công cụ khác. Một ví dụ với xmlstartlet:

 
% xmlstarlet escape 
<?xml version="1.0" encoding="utf-8"?> 
<abc> 
    <def ghi="jkl"> 
    mnop 
    </def> 
</abc> 
[Control-D] 
&lt;?xml version="1.0" encoding="utf-8"?&gt; 
&lt;abc&gt; 
    &lt;def ghi="jkl"&gt; 
    mnop 
    &lt;/def&gt; 
&lt;/abc&gt; 
+0

Thật không may, điều này nằm ngoài những gì tôi đang yêu cầu. Gần đây, tôi đã thấy các dịch vụ web lấy một chuỗi như một tham số. Điều tệ hơn là tham số chuỗi đang lấy XML làm đầu vào. Mỗi tính năng được yêu cầu, tôi cần chuyển đổi một tài liệu XML thành một phong bì SOAP. Vấn đề là tôi cần phải chuyển đổi tài liệu XML thành một văn bản đã thoát để chuyển nó thành một tham số chuỗi của phong bì SOAP (cho rằng mọi thứ khác là tĩnh). Do đó, vấn đề tôi hỏi sẽ xuất hiện nếu bạn không sử dụng một thư viện lớp proxy khác ở giữa. –

0

Nếu bạn có quyền truy cập vào nó, tôi muốn giới thiệu các Saxon gia hạn serialize. Nó thực hiện chính xác những gì bạn muốn nó làm. Nếu bạn không muốn làm điều đó, bạn sẽ phải chèn các tham chiếu thực thể theo cách thủ công khi bạn tạo tài liệu. Nó sẽ trở nên giòn, nhưng nó sẽ hoạt động đối với hầu hết các tài liệu:

<xsl:template match="/"> 
    <TestElement> 
     <xsl:apply-templates/> 
    </TestElement> 
</xsl:template> 
<xsl:template match="*"> 
    <xsl:text>&lt;</xsl:text> 
    <xsl:value-of select="name()"/> 
    <xsl:apply-templates select="@*"/> 
    <xsl:text>&gt;</xsl:text> 
    <xsl:apply-templates select="node()"/> 
    <xsl:text>&lt;/</xsl:text> 
    <xsl:value-of select="name()"/> 
    <xsl:text>&gt;</xsl:text> 
</xsl:template> 
<xsl:template match="@*"> 
    <xsl:text>&#32;</xsl:text> 
    <xsl:value-of select="name()"/> 
    <xsl:text>="</xsl:text> 
    <xsl:value-of select="."/> 
    <xsl:text>"</xsl:text> 
</xsl:template> 
<xsl:template match="text()"> 
    <xsl:value-of select="."/> 
</xsl:template> 

Đáng chú ý nhất, điều này có thể sẽ vỡ nếu thuộc tính của bạn có ký tự ngoặc kép. Nó thực sự tốt hơn để sử dụng saxon, hoặc sử dụng một mở rộng người dùng bằng văn bản sử dụng một serializer thích hợp nếu bạn không thể.

16

thay vì thoát, bạn có thể thêm văn bản bên trong phần CDATA. Văn bản bên trong phần CDATA sẽ bị trình phân tích cú pháp bỏ qua, tương tự như khi nó được thoát.

dụ của bạn sẽ trông như thế này

<TestElement> 
<![CDATA[ 
<abc> 
    <def ghi="jkl"> 
    mnop 
    </def> 
</abc> 
]]> 
</TestElement> 

sử dụng đoạn mã XSLT sau:

<xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text> 
<xsl:copy-of select="/"/> 
<xsl:text disable-output-escaping="yes">]]</xsl:text> 
<xsl:text disable-output-escaping="yes">&gt;</xsl:text> 
+0

+1 để trợ giúp tôi về vấn đề của tôi (tách biệt với câu hỏi này). –

+0

Điều này cũng rất hữu ích đối với tôi. Nó có vẻ như là một mẹo tốt hơn so với serializer Saxon. –

+0

Nó không làm việc cho tôi mặc dù với đầu ra HTML. Tài liệu trông OK nhưng bằng cách nào đó trình duyệt từ chối hiển thị văn bản. Tôi nhận được ]]> Nhưng trình duyệt duy nhất cho thấy "]]>". Tôi giả định đây có thể là một vấn đề HTML. Ai đó có thể xác nhận điều đó? –

4

Bất cứ ai quan tâm đến việc cấp phép nhập nhằng khi sử dụng lại các đoạn mã từ stack overflow có thể quan tâm đến những điều sau 3 -như mã được cấp phép BSD, có vẻ như thực hiện những gì được yêu cầu bởi người đăng gốc:

http://lenzconsulting.com/xml-to-string/

1

Bạn có thể ngăn các nút namespace thêm bằng cách thêm một thử nghiệm trong đầu ra không gian tên:


<xsl:variable name="curnode" select="."/> 
    <xsl:for-each select="namespace::*"> 
     <xsl:variable name="nsuri" select="."/> 
     <xsl:if test="$curnode/descendant-or-self::*[namespace-uri()=$nsuri]"> 
     ... 
3

Tôi đã cố gắng để thực hiện các câu trả lời được cung cấp bởi Pavel Minaev và muốn chỉ ra rằng điều này là rất nguy hiểm cho các chuỗi lớn vì mỗi ký tự trong chuỗi đầu vào được đệ quy trên từng cá nhân, làm cho độ sâu đệ quy nhanh chóng hết. Tôi đã cố gắng để chạy nó trên một vài dòng văn bản và nó gây ra một tràn ngăn xếp (lol).

Thay vào đó, tôi sử dụng mẫu không cần kiểm tra từng chữ cái riêng biệt, thay vào đó nó sẽ đặt văn bản cho đến khi tìm thấy chuỗi cần được thay thế. Điều này sau đó có thể được sử dụng để thoát khỏi nhân vật:

<xsl:template name="Search-And-Replace"> 
    <xsl:param name="Input-String"/> 
    <xsl:param name="Search-String"/> 
    <xsl:param name="Replace-String"/> 
    <xsl:choose> 
     <xsl:when test="$Search-String and contains($Input-String, $Search-String)"> 
      <xsl:value-of select="substring-before($Input-String, $Search-String)"/> 
      <xsl:value-of select="$Replace-String"/>   
      <xsl:call-template name="Search-And-Replace"> 
       <xsl:with-param name="Input-String" select="substring-after($Input-String, $Search-String)"/> 
       <xsl:with-param name="Search-String" select="$Search-String"/> 
       <xsl:with-param name="Replace-String" select="$Replace-String"/> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:value-of select="$Input-String"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

Sau đó chỉ cần nó là một vấn đề gọi là khuôn mẫu cho các char mà bạn muốn chạy trốn ..

<xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="Hi I am a string &amp; I am awesome"/> 
      <xsl:with-param name="Search-String" select="'&amp;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;amp;'"/> 
    </xsl:call-template> 

Để thoát nhiều ký tự trong một chuỗi, tôi đã sử dụng mẫu trình bao bọc sử dụng các biến ...

<xsl:template name="EscapeText"> 
    <xsl:param name="text" /> 

    <xsl:variable name="a"> 
    <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$text"/> 
      <xsl:with-param name="Search-String" select="'&amp;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;amp;'"/> 
     </xsl:call-template>    
    </xsl:variable> 

    <xsl:variable name="b">  
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$a"/> 
      <xsl:with-param name="Search-String" select="'&quot;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;quot;'"/> 
     </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="c">  
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$b"/> 
      <xsl:with-param name="Search-String">&apos;</xsl:with-param> 
      <xsl:with-param name="Replace-String" select="'&amp;apos;'"/> 
     </xsl:call-template> 
    </xsl:variable>   

    <xsl:variable name="d">  
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$c"/> 
      <xsl:with-param name="Search-String" select="'&gt;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;gt;'"/> 
     </xsl:call-template> 
    </xsl:variable> 

    <xsl:variable name="e"> 
     <xsl:call-template name="Search-And-Replace"> 
      <xsl:with-param name="Input-String" select="$d"/> 
      <xsl:with-param name="Search-String" select="'&lt;'"/> 
      <xsl:with-param name="Replace-String" select="'&amp;lt;'"/> 
     </xsl:call-template> 
    </xsl:variable>  
    <!--this is the final output--> 
    <xsl:value-of select="$e"/>  
</xsl:template> 

Điều này được chứng minh là an toàn hơn nhiều đối với các chuỗi lớn vì nó không còn phải recurse cho mỗi ký tự riêng lẻ trong chuỗi đầu vào.

+0

Chuỗi dài của tôi đã phá vỡ giải pháp đệ quy ký tự riêng lẻ.Giải pháp này làm việc tốt cho tôi nhưng tôi phải thêm một 'disable-output-escaping' vào đầu ra cuối cùng:' '. Cảm ơn! – twamley

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