【问题标题】:JSON inside an html element attribute built via XSLT通过 XSLT 构建的 html 元素属性中的 JSON
【发布时间】:2015-12-26 07:02:15
【问题描述】:

巨大的免责声明:这仅适用于您处理生成的 html 表单时使用的内容不能原生翻译 HTML 实体双引号的内容,否则 XSLT 处理器应该负责生成即使在正常的双引号包裹的属性。 (例如")。我当然意识到只有在我写完之后,转义的双引号才真正满足我的需要,因为我尝试的第一件事是单引号,它没有解析为 JSON,而且我之前遇到过敏感的 JSON 解析情况所以我什至没想过只使用元素转义的双引号,但这可能仍然有利于其他一些情况(请参阅:敏感的 JSON 解析情况),所以我将继续发布它。

简而言之:您不需要为 JSON 执行所有这些操作,它将由基于现代浏览器的解析器执行。 HTML 实体双引号 (") 是它们的有效分隔符(是的,甚至是 IE)。


我遇到了一个令人沮丧的难题,即通过 XSLT 将 JSON 构建到 HTML 元素的属性中(特别是用于初始化滑块的 HTML5 数据属性)。

XSLT 只处理包含在双引号内的属性。 (注意:如果有一个可以交换它的处理标志/指令,它必须是我可以从工作表内部调用的,而不是从对处理器的发起调用中调用的,因为我无法控制它)。

JSON 需要双引号才有效。

关键处理细节:我在 Saxon 9.3.0.5 中工作,它被一个我无法直接访问修改的系统调用和初始化(专有 CMS 代码)。

我可能(?)可以在 javascript 中解决这个问题,但如果有一个我忽略的原生 XSLT 解决方案,那么很高兴知道。我假设没有,因为有一个 comment from Michael Kay 从 2013 年开始,他开玩笑说属性包装选择一直是不可变的,因为应该没有任何世俗的理由需要来改变它。到目前为止,我通常会同意,因为在双引号属性中,JSON 的单引号实际上是无效的 JSON。 编辑:请参阅免责声明,他可能仍然适用于 99% 的 JSON 相关案例。

我可能会在脚本元素或类似元素中构建一个单独的 JSON 字符串,然后在相关的滑块函数在此运行之前通过 javascript 将其注入属性中,这可能是我应该当我重构这个时做,但我想知道还有哪些其他选项,如果有的话。

我将使用不是真正原生/正确的 XSLT 解决方案来解决这个问题,该解决方案对于遇到类似问题的其他人来说非常有用,但我真的想要更好的东西,如果它出来了在那里,因为我讨厌在 XSL 中使用这种类型的 hack。

示例代码(减去很多周围的匹配项、身份 xform 等):

(问题特定于像这样将“data-slick”属性添加到匹配的ul)

<xsl:template match="mus:call-template[@name='slick-slider-carousel']//ul">
    <xsl:param name="slider-options" tunnel="yes" />
    <xsl:param name="pcf-context" tunnel="yes" />
    <xsl:copy copy-namespaces="no">
        <xsl:apply-templates select="@attribute" />
        <xsl:attribute name="data-slick"><xsl:text>{</xsl:text>
            <xsl:apply-templates select="$slider-options" />
            <xsl:text>}</xsl:text>
        </xsl:attribute>
        <xsl:apply-templates select="$pcf-context/gallery/images/image" mode="slick-slider">
            <xsl:with-param name="asset-id" select="$pcf-context/gallery/@asset-id" />
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

示例 html 布局表:

    <mus:call-template name="slick-slider-carousel">
        <div class="image-carousel">
            <ul>
            </ul>
        </div>
    </mus:call-template>

从内容表加载到 $pcf-context 的示例树片段(通过 xsl:sequence):

<gallery created="2015-03-25T21:18:20Z" forceCrop="false" modified="2015-03-25T21:19:31Z" asset_id="649">
  <name>Test</name>
  <thumbnailWidth>100</thumbnailWidth>
  <thumbnailHeight>100</thumbnailHeight>
  <forceCrop>false</forceCrop>
  <advanced>
  </advanced>
  <images>
    <image created="2015-03-25T21:18:53Z" modified="2015-03-25T21:18:53Z" staging_url="[full-url]/6d640b3f-7e50-4bdf-802d-2d5e33955ed1.jpeg" url="[full-url]/6d640b3f-7e50-4bdf-802d-2d5e33955ed1.jpeg" status="existing">
      <storedName>6d640b3f-7e50-4bdf-802d-2d5e33955ed1.jpeg</storedName>
      <friendlyName>d01454b9-5eab-4a7f-b999-3ec92313b193.jpeg</friendlyName>
      <thumbnail staging_url="[full-url]/thumb/6d640b3f-7e50-4bdf-802d-2d5e33955ed1.jpeg" url="[full-url]/6d640b3f-7e50-4bdf-802d-2d5e33955ed1.jpeg">
        <width>100</width>
        <height>100</height>
      </thumbnail>
      <title>A very cute puppy</title>
      <description>Cute puppy</description>
      <caption>Look at that cute puppy! lorem ipsum blah blah blah</caption>
      <link>
      </link>
    </image>
  </images>
</gallery>

【问题讨论】:

    标签: json xslt xslt-2.0


    【解决方案1】:

    手动构建具有属性结构的 HTML 元素(在属性值上使用单引号外包装分隔符)

    这是一个 hack,因为它不是在结果树中生成元素,而是在浏览器加载输出表时生成充当 DOM 节点的文本……但这些 实际上仍然不是 XSLT 处理器中的元素。我真的不建议你使用这种方法,除非你绝对需要它:

    <xsl:template match="mus:call-template[@name='slick-slider-carousel']//ul">
        <xsl:param name="slider-options" tunnel="yes" />
        <xsl:param name="pcf-context" tunnel="yes" />
        <xsl:text disable-output-escaping="yes">&lt;ul</xsl:text>
            <xsl:apply-templates select="attribute()" mode="textify" />
            <xsl:text> data-slick='{</xsl:text>
            <xsl:apply-templates select="$slider-options" />
            <xsl:text>}'</xsl:text>
            <xsl:text disable-output-escaping="yes">&gt;</xsl:text>
            <xsl:apply-templates select="$pcf-context/gallery/images/image" mode="slick-slider">
                <xsl:with-param name="asset-id" select="$pcf-context/gallery/@asset-id" />
            </xsl:apply-templates>
        <xsl:text disable-output-escaping="yes">&lt;/ul&gt;</xsl:text>
    </xsl:template>
    
    <xsl:template match="attribute()" mode="textify">
        <xsl:text> </xsl:text>
        <xsl:value-of select="local-name()" />
        <xsl:text>='</xsl:text>
        <xsl:value-of select="." />
        <xsl:text>'</xsl:text>
    </xsl:template>
    

    注意点:

    1. disable-output-escaping:您需要在要生成 html 元素的文本节点上使用它,或者更具体地说,在它们的左方括号和右方括号(lt 和 gt)上。
    2. 如果您在其中使用应用模板,请非常了解结果将如何交互。例如,任何试图将其属性添加到当前元素的属性模板(通过身份转换等)要么失败,要么将其添加到父元素,因为此处创建的 html 元素实际上不是 XSLT 元素,而不是附加属性的有效目标。如果您想使用应用模板,您需要有一个替代模式匹配,将它们转储到文本中,而不是将它们作为节点进行操作(请参阅包含的示例,其中包含属性的“textify”模式)。
    3. 根据 2,您希望添加的任何属性都必须手动构建。注意空格! (在此处查看主比赛中@data-slick 的构建)
    4. 使用&lt;xsl:text&gt; 对您有利:如果使用得当,这将允许您在xsl:text 元素之间为您的代码提供一些行距,这将使其比单个&lt;xsl:value-of select="concat(stuff, more stuff, too much more stuff, etc)" /&gt; 更易读
    5. (个人偏好)如果要继承已经存在的属性(身份转换样式),请使用模态apply-templates 而不是for-each。它不仅更简洁,更像是一种声明性范式,而且更易于重用。请注意,如果您手动构建的任何属性已经存在,您将需要提供一个逻辑路径来排除它们(例如特定于删除它的属性的模板匹配)或提供一种方法来手动添加它们,如果如果存在,则不显示或附加他们的信息(可以通过多种方式完成,我个人仍然觉得apply-templates 的属性处理方法对他们中的大多数人来说更干净)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-13
      • 2020-01-01
      • 1970-01-01
      • 2013-01-01
      • 2011-11-30
      • 2020-06-18
      • 2012-09-01
      • 2020-09-11
      相关资源
      最近更新 更多