【问题标题】:How to convert specific attribute into element in complex XML如何将特定属性转换为复杂 XML 中的元素
【发布时间】:2012-01-03 13:48:22
【问题描述】:

在我之前的question 中,我询问了如何将特定属性转换为简单 XML 中的元素。现在我有更复杂的输入。 我需要将属性“查询”转换为元素。 复杂输入:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<filter query="select" name="hello" description="world">
    <certification>WFA</certification>
    <uuid>fd5d9f15-f6d9-4e71-aaf4-024aaaa627f2</uuid>
    <parameters>
        <parameter type="STRING" name="name" label="name">
            <description>Some name</description>
        </parameter>
    </parameters>
    <returned-attributes>
        <returned-attribute>id</returned-attribute>
    </returned-attributes>
</filter>

我的愿望输出如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<filter name="hello" description="world">
    <certification>WFA</certification>
    <uuid>fd5d9f15-f6d9-4e71-aaf4-024aaaa627f2</uuid>
    <query>select<query/>
    <parameters>
        <parameter type="STRING" name="name" label="name">
            <description>Some name</description>
        </parameter>
    </parameters>
    <returned-attributes>
        <returned-attribute>id</returned-attribute>
    </returned-attributes>
</filter>

我使用以下 XSLT:

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

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

    <!-- match the filter element -->
    <xsl:template match="filter">
        <xsl:choose>
            <xsl:when test="/filter/@query">
                <!-- output a filter element -->
                <xsl:element name="filter">

                    <!-- add the name attribute, using the source name attribute value -->
                    <xsl:attribute name="name">
                        <xsl:value-of select="@name"/>
                    </xsl:attribute>

                    <!-- add the description attribute (if found), using the source name attribute value -->
                    <xsl:choose>
                        <xsl:when test="/filter/@description">
                            <xsl:attribute name="description">
                                <xsl:value-of select="@description"/>
                            </xsl:attribute>
                        </xsl:when>
                    </xsl:choose>

                    <!-- add the query as child element, using the source query attribute value -->
                    <xsl:element name="query">
                        <xsl:value-of select="@query"/>
                    </xsl:element>

                    <!-- add all common elements -->
                    <xsl:element name="certification">
                        <xsl:value-of select="certification"/>
                    </xsl:element>
                    <xsl:element name="uuid">
                        <xsl:value-of select="uuid"/>
                    </xsl:element>

                    <!-- copy parameters -->
                    <xsl:copy>
                        <xsl:apply-templates select="/filter/parameters"/>
                    </xsl:copy>

                    <!-- copy attributes -->
                    <xsl:copy>
                        <xsl:apply-templates select="/filter/returned-attributes"/>
                    </xsl:copy>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

转换有效,但在我看来很复杂。 请注意,我使用 if/else 逻辑,因为我的输入可以包含“旧”(未转换)和 “新”(转换后的)XML 文件。

请指教。提前致谢。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    这是一个简短的解决方案

    <xsl:stylesheet version="1.0"
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
         <xsl:output omit-xml-declaration="yes" indent="yes"/>
         <xsl:strip-space elements="*"/>
    
         <xsl:template match="node()|@*">
             <xsl:copy>
               <xsl:apply-templates select="node()|@*"/>
             </xsl:copy>
         </xsl:template>
    
         <xsl:template match="parameters">
             <xsl:apply-templates select="/filter/@query" mode="build"/>
             <xsl:copy>
               <xsl:apply-templates select="node()|@*"/>
             </xsl:copy>
         </xsl:template>
         <xsl:template match="/filter/@query"/>
    
         <xsl:template match="/filter/@query" mode="build">
             <query><xsl:value-of select="."/></query>
         </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时

    <filter query="select" name="hello" description="world">
        <certification>WFA</certification>
        <uuid>fd5d9f15-f6d9-4e71-aaf4-024aaaa627f2</uuid>
        <parameters>
            <parameter type="STRING" name="name" label="name">
                <description>Some name</description>
            </parameter>
        </parameters>
        <returned-attributes>
            <returned-attribute>id</returned-attribute>
        </returned-attributes>
    </filter>
    

    产生想要的正确结果

    <filter name="hello" description="world">
       <certification>WFA</certification>
       <uuid>fd5d9f15-f6d9-4e71-aaf4-024aaaa627f2</uuid>
       <query>select</query>
       <parameters>
          <parameter type="STRING" name="name" label="name">
             <description>Some name</description>
          </parameter>
       </parameters>
       <returned-attributes>
          <returned-attribute>id</returned-attribute>
       </returned-attributes>
    </filter>
    

    【讨论】:

    • 感谢@Dimitre Novatchev。它工作正常。我刚刚用“when”逻辑包装了查询块,以支持旧/新 XML 文件。
    • @SashaKorman:几乎总是可以避免显式的 xslt 条件指令,而只使用模板和模式匹配。在这种情况下,我将使用:&lt;xsl:apply-templates select="/filter/@query" mode="build"/&gt; 并将拥有:&lt;xsl:template match="/filter/@query" mode="build"&gt;&lt;query&gt; &lt;xsl:value-of select="."/&gt;&lt;/xsl:template&gt;
    • 你的意思是像下面的 XSLT?它不起作用 w3.org/1999/XSL/Transform"> ">
    • @SashaKorman:我刚刚用满足您的新要求的解决方案更新了我的答案——完全符合我在评论中提出的代码——而且效果很好。
    【解决方案2】:

    这应该可以解决问题:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
       <!-- copy all content other than the query attribute -->
       <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*[not(name()='query')]"/>
            </xsl:copy>
       </xsl:template>
    
       <!-- match uuid so that we can insert query after -->
       <xsl:template match="uuid">
          <xsl:copy>
             <xsl:apply-templates select="@*|node()"/>
           </xsl:copy>
           <!-- add our query element -->
           <xsl:element name="query">
             <!-- navigate up one level to reach the query attribute -->
             <xsl:value-of select="../@query"/>
           </xsl:element>     
       </xsl:template>
    </xsl:stylesheet>
    

    快速提示,了解identity transform 及其工作原理。

    【讨论】:

    • 建议的解决方案存在逻辑错误。在给定的 XML 文档上,它会产生想要的结果,但是在具有类似结构的其他文档上,它可能会在不需要时删除属性。请正确。
    • 谢谢@ColinE。这个对我有用。我刚刚为“查询”元素创建添加了“何时”逻辑,以支持已转换的 XML 文件。
    • @Dimitre Novatchev 能否提供一个此 XSLT 可以删除属性的示例
    • @SashaKorman:任何类似于您提供的 XML 文档,在除顶部 filter 元素之外的元素上具有 query 属性,并且也不打算删除.
    猜你喜欢
    • 1970-01-01
    • 2017-06-17
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2010-10-13
    • 1970-01-01
    相关资源
    最近更新 更多