【问题标题】:How to convert nodes in XML to CDATA with XSLT?如何使用 XSLT 将 XML 中的节点转换为 CDATA?
【发布时间】:2021-02-28 13:35:34
【问题描述】:

我有一个source.xml 文件,其结构如下:

<products>
    <product>
        <id>1</id>
        <description>
            <style>
            table{
            some css here
            }
            </style>
            <descr>
            <div>name of producer like ABC&DEF</div>
            <table>
                <th>parameters</th>
                <tr><td>name of param 1 e.g POWER CONSUMPTION</td>
                    <td>value of param 1 with e.g < 100 W</td></tr>
            </table>
            </descr>
        </description>
    </product>
.....................
</products>

我想要:

<products>
    <product>
        <id>1</id>
        <description>
        <![CDATA[
            <style>
            table{
            some css here
            }
            </style>
            <descr>
            <div>name of producer like ABC&DEF</div>
            <table>
                <th>parameters</th>
                <tr><td>name of param 1 e.g POWER CONSUMPTION</td>
                    <td>value of param 1 with e.g < 100 VA</td></tr>
            </table>
        ]]>
            </descr>
        </description>
    </product>
.....................
</products>

我尝试了.xsl 样式表,基于: How to use in XSLT? 和 Add CDATA to an xml file 和 how to add cdata to an xml file using xsl 如:

<?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" indent="yes" encoding="UTF-8" />

<xsl:template match="/products">
    <products>
    <xsl:for-each select="product">
        <product>
            <description>
            <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
            <xsl:copy-of select="description/node()" />    
            <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
            </xsl:for-each>
            </description>
        </product>
    </xsl:for-each>
    </products>
</xsl:template>
</xsl:stylesheet>

和

<?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" indent="yes" cdata-section-elements="description"/>

  <xsl:template match="description">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:variable name="subElementsText">
        <xsl:apply-templates select="node()" mode="asText"/>
      </xsl:variable>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="text()" mode="asText">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="*" mode="asText">
    <xsl:value-of select="concat('&lt;',name())"/>
    <xsl:for-each select="@*">
      <xsl:value-of select="concat(' ',name(),'=&quot;',.,'&quot;')"/>
    </xsl:for-each>
    <xsl:value-of select="'&gt;'"/>
    <xsl:apply-templates select="node()" mode="asText"/>
    <xsl:value-of select="concat('&lt;/',name(),'&gt;')"/>
  </xsl:template>

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

</xsl:stylesheet>

但运行我的 python 脚本

import lxml.etree as ET

doc = ET.parse('source.xml')
xslt = ET.parse('modyfi.xsl')
transform = ET.XSLT(xslt)
newdoc = transform(doc)
with open(f'output.xml', 'wb') as f:
    f.write(newdoc)

在 SublimeText3 上我总是遇到同样的错误:

lxml.etree.XMLSyntaxError: StartTag: invalid element name, {number of line and column with first appearance of illegal character}

我敢肯定,在上面的链接中,该解决方案就在我面前,但我看不到它。 或者也许我找不到它,因为我无法提出正确的问题。请帮忙,我是编码新手。

【问题讨论】:

  • 请编辑您的帖子并添加您尝试过的 XSLT。
  • 谢谢@YitzhakKhabinsky。我添加了.xsl 我尝试过的样式表。

标签: python xml xslt lxml cdata


【解决方案1】:

输入的 XML 格式不正确。我必须先修复它。这似乎是它最终失败的原因。

XML

<products>
    <product>
        <id>1</id>
        <description>
            <style>table{
            some css here
            }</style>
            <descr>
                <div>name of producer like ABC&amp;DEF</div>
                <table>
                    <th>parameters</th>
                    <tr>
                        <td>name of param 1 e.g POWER CONSUMPTION</td>
                        <td>value of param 1 with e.g &lt; 100 W</td>
                    </tr>
                </table>
            </descr>
        </description>
    </product>
</products>

XSLT

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="description">
        <xsl:copy>
            <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
            <xsl:copy-of select="*"/>
            <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

输出

<products>
  <product>
    <id>1</id>
    <description><![CDATA[
      <style>table{
            some css here
            }
      </style>
      <descr>
        <div>name of producer like ABC&amp;DEF</div>
        <table>
          <th>parameters</th>
          <tr>
            <td>name of param 1 e.g POWER CONSUMPTION</td>
            <td>value of param 1 with e.g &lt; 100 W</td>
          </tr>
        </table>
      </descr>]]>
    </description>
  </product>
</products>

【讨论】:

  • 谢谢。这是否意味着我不能在 CDATA 部分中使用 '&' 和 '
  • @JWPB,没有它们,输入的 XML 格式不正确。因此 XSLT 无法处理。
  • 明白。所以我想要的有问题的输出内容是不可能获得的。这就解释了我失败的原因。
  • 转换我的问题信息表单开头显示的 xml 文件的正确方法是什么?我能够将我的 xml 文件(例如 &amp;lt;td&amp;gt; 和 &amp;amp; 转换为 &amp;lt;td&amp;gt; 和 &amp;amp; 所以现在 &lt;description&gt; 元素内的所有内容都被转义了。但现在我不知道如何“非转义”html标签字符,同时将转义字符留在这些标签的文本中。
【解决方案2】:

在我看来,一种干净的方法是使用序列化函数将您想要的所有元素序列化为纯文本,然后在 cdata-section-elements 中的 xsl:output 声明中指定父容器,最后确保XSLT 处理器负责序列化。

现在 XSLT 3 有一个内置的 XPath 3.1 serialize 函数,在 Python 中,您可以将它与 Saxon-C 及其 Python API 一起使用。

对于带有 lxml 的基于 libxslt 的 XSLT 1,您可以在 Python 中编写一个暴露给 XSLT 的扩展函数:

from lxml import etree as ET

def serialize(context, nodes):
    return b''.join(ET.tostring(node) for node in nodes)


ns = ET.FunctionNamespace('http://example.com/mf')
ns['serialize'] = serialize

xml = ET.fromstring('<root><div><p>foo</p><p>bar</p></div></root>')

xsl = ET.fromstring('''<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mf="http://example.com/mf" version="1.0">
  <xsl:output method="xml" cdata-section-elements="div" encoding="UTF-8"/>
  <xsl:template match="@* | node()">
    <xsl:copy>
       <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="div">
    <xsl:copy>
      <xsl:value-of select="mf:serialize(node())"/>
    </xsl:copy>
 </xsl:template>
</xsl:stylesheet>''')

transform = ET.XSLT(xsl)

result = transform(xml)

result.write_output("transformed.xml")

然后输出是

<?xml version="1.0" encoding="UTF-8"?>
<root><div><![CDATA[<p>foo</p><p>bar</p>]]></div></root>

【讨论】:

  • 谢谢@Martin Honnen。我想我需要几天时间才能理解你写的东西。我会试试的。
猜你喜欢
  • 2013-02-17
  • 2014-07-16
  • 2011-10-05
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
  • 2018-03-09
相关资源
最近更新 更多