XSLT 通用将结构值类型转换为单值字符串

Posted

技术标签:

【中文标题】XSLT 通用将结构值类型转换为单值字符串【英文标题】:XSLT Generic transform structural value type to single valued string 【发布时间】:2020-02-14 07:41:48 【问题描述】:

我想创建一个 xslt 转换,它将基于结构的属性转换为单值字符串,最好是通用的,这样就不会从输入中引用命名项。

示例输入:

<attr attr-name="items">
        <value type="structured">
          <component name="studentnummer">001001</component>
          <component name="achternaam">Bimans</component>
          <component name="voorletters">L./component>
          <component name="roepnaam">Leo</component>
          <component name="geboortedatum">09-08-1986</component>
          <component name="geslacht">V</component>
          <component name="mobiel_telefoonnummer">0612345678</component>
          <component name="voertaal_nl">Nederlands</component>
          <component name="voertaal_en">Dutch</component>
          <component name="extern_emailadres">L.Bimans@domain.nl</component>
        </value>
        <value type="structured">
          <component name="studentnummer">001002</component>
          <component name="achternaam">Boels</component>
          <component name="voorletters">F.</component>
          <component name="roepnaam">Felix</component>
          <component name="geboortedatum">04-02-1993</component>
          <component name="geslacht">M</component>
          <component name="voertaal_nl">Nederlands</component>
          <component name="voertaal_en">Dutch</component>
        </value>
</attr>

应该翻译成:

<attr attr-name="items">
    <value type="string">#studentnummer#001001#achternaam#Bimans#voorletters#L.#roepnaam#Leo#geboortedatum#09-08-1986#geslacht#V#mobiel_telefoonnummer#0612345678#voertaal_nl#Nederlands#voertaal_en#Dutch#extern_emailadres#L.Bimans@domain.nl></value>
    <value type="string">#studentnummer#001002#achternaam#Boels#voorletters#F.#roepnaam#Felix#geboortedatum#04-02-1993#geslacht#M#voertaal_nl#Nederlands#voertaal_en#Dutch</value>
<attr>

另一个例子:

<attr attr-name="links">
        <value type="structured">
          <component name="rel">self</component>
          <component name="href">http://192.83.206.98:9999/rds/basis/studenten/</component>
        </value>
        <value type="structured">
          <component name="rel">edit</component>
          <component name="href">http://192.83.206.98:9999/rds/basis/studenten/</component>
        </value>
        <value type="structured">
          <component name="rel">describedby</component>
          <component name="href">http://192.83.206.98:9999/rds/metadata-catalog/basis/studenten/</component>
        </value>
</attr>

应该翻译成:

<attr attr-name="links">
    <value type="string">#rel#self#href#http://192.83.206.98:9999/rds/basis/studenten/</value>
    <value type="string">#rel#edit#href#http://192.83.206.98:9999/rds/basis/studenten/</value>
    <value type="string">#rel#describedby#href#http://192.83.206.98:9999/rds/metadata-catalog/basis/studenten/</value>
</attr>

转换应尽可能通用。换句话说,相同的代码可以用于两种类型的输入数据(如果可能的话)。因此,没有对属性名称、值或任何内容的引用。 请注意,组件名称(来自结构)应该(最好)包含在结果字符串中(如示例所示),然后是实际值(全部由分隔符 # 分隔)。

一直在努力实现这一点,但到目前为止还没有运气。

还要注意我只能使用 xslt 1.0!

谢谢!

【问题讨论】:

展示你的最佳尝试总是有用的。然后我们可以看到你已经知道多少以及症结在哪里。 【参考方案1】:

一般来说,我首先从身份开始复制现有的 XML:

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

然后有一个模板匹配任何具有type="structured" 属性的元素(即你的所有值元素)。对于匹配的那些,我们将复制元素并添加type="string" 属性。我们将该元素的文本设置为由## 加上名称属性的值和每个子元素的文本组成。像这样:

<xsl:template match="*[@type = 'structured']">
  <xsl:copy>
    <xsl:attribute name="type">string</xsl:attribute>
    <xsl:for-each select="*">##<xsl:value-of select="@name" />##<xsl:value-of select="text()" /></xsl:for-each>
  </xsl:copy>
</xsl:template>

这足够通用,您真正需要的只是一个具有 type="structure" 的节点和具有 name 属性的子节点。

【讨论】:

谢谢!这只是我正在寻找的代码。测试了它;它有效!【参考方案2】:

由于您不希望有任何元素和属性名称,因此问题出现了:“我们要如何识别包含我们的值的节点(元素和属性)?”

据我所知,唯一的其他方法是假定源树中的固定节点位置。

只要结构保持不变,以下 XSLT 就可以做到:

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

    <xsl:output 
        method="xml"
        indent="yes"/>

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


    <!-- node()[self::*] tests for an element node-type, 
         thus we only process element nodes, this way -->
    <xsl:template match="/node()[self::*]">
        <!-- we create a new element, that has its name set 
             to the name of the current node, indicated by '.' -->
        <xsl:element name="name(.)">
            <!-- We now create an attribute for the element we are creating.
                 Handling the attribute like this is only going 
                 to work out the way we want it, if the attribute,
                 whose  name we want to replicate is the first attribute 
                 of the element. -->
            <xsl:attribute name="name(attribute::node())">
                <xsl:value-of select="attribute::node()"/>
            </xsl:attribute>
            <!-- within our current node (which is 'attr') we iterate 
                 through each contained node and create an output element, which will
                 be the 'value' element. -->
            <xsl:for-each select="node()[self::*]">
                <xsl:element name="name(.)">
                    <xsl:attribute name="type">string</xsl:attribute>
                    <!-- within each 'value' element, we iterate through all 'component'
                         elements, constructing out text node dynamically. -->
                    <xsl:for-each select="node()[self::*]">
                        <xsl:text>#</xsl:text>
                        <xsl:value-of select="attribute::node()"/>
                        <xsl:text>#</xsl:text>
                        <xsl:value-of select="."/>
                    </xsl:for-each>
                </xsl:element>
            </xsl:for-each>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>

    <!-- we suppress any built-in templates for text-node processing, otherwise we
         would get them in the output, too. -->
    <xsl:template match="text()"/>

</xsl:stylesheet>

附:我不知道您为什么受限于使用 XSLT-1.0,但您也可以从 http://saxonica.com 获得 Saxon 程序。 SaxonHE(见下载)版本是开源的,可以免费使用。

【讨论】:

以上是关于XSLT 通用将结构值类型转换为单值字符串的主要内容,如果未能解决你的问题,请参考以下文章

无法将协议的通用关联类型的值转换为预期的参数类型

如何在Marklogic或Couchbase中进行xslt类型转换?

“字符串”的值无法转换为“T”类型。获取查询字符串的通用函数?

通过字符串名称类型转换为特定的结构类型

是否有任何通用 Parse() 函数可以使用解析将字符串转换为任何类型?

从通用值到字符串的 C# 转换问题