XSLT Change XML elements order

2019-07-29 18:28发布

I need to rearrange the source XML file with my transformation to obtain:

  1. A new elements order. Look the difference between source and desired output: <gmd:role> should appear AFTER <contactInfo> and elements inside <CI_Contact> have to appear in different order too.
  2. Add elements that are not present in the source as empty elements in the output otherwise copy the element as-is (look at the <address> element)

Source file:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="c:\ISO19139_rve.xsl"?>
<MD_Metadata xmlns="http://www.isotc211.org/schemas/2005/gmd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gco="http://www.isotc211.org/schemas/2005/gco" xmlns:gml="http://www.opengis.net/gml" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.isotc211.org/schemas/2005/gmd/gmd.xsd">
<CI_ResponsibleParty>
    <organizationName>
        <gco:CharacterString>Organisation Name</gco:CharacterString>
    </organizationName>
    <role>
        <CI_RoleCode codeList="./resource/codeList.xml#CI_RoleCode" codeListValue="Proprietario">Proprietario</CI_RoleCode>
    </role>
    <contactInfo>
        <CI_Contact>
            <onlineResource>
                <CI_OnlineResource>
                    <linkage>
                        <URL>http://www.mydomain.it</URL>
                    </linkage>
                </CI_OnlineResource>
            </onlineResource>
            <phone>
                <CI_Telephone>
                    <voice>
                        <gco:CharacterString>+39 123 456789</gco:CharacterString>
                    </voice>
                </CI_Telephone>
            </phone>
        </CI_Contact>
    </contactInfo>
</CI_ResponsibleParty>
</MD_Metadata>

Desired result:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="c:\ISO19139_rve.xsl"?>
<MD_Metadata xmlns="http://www.isotc211.org/schemas/2005/gmd"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:gco="http://www.isotc211.org/schemas/2005/gco"
             xmlns:gml="http://www.opengis.net/gml"
             xmlns:xlink="http://www.w3.org/1999/xlink"
             xsi:schemaLocation="http://www.isotc211.org/schemas/2005/gmd/gmd.xsd">
<gmd:CI_ResponsibleParty>
  <gmd:organisationName>
    <gco:CharacterString>Organisation Name</gco:CharacterString>
  </gmd:organisationName>
  <gmd:contactInfo>
    <gmd:CI_Contact>
      <gmd:phone>
        <gmd:CI_Telephone>
          <gmd:voice>
            <gco:CharacterString>+39 123 456789</gco:CharacterString>
          </gmd:voice>
        </gmd:CI_Telephone>
      </gmd:phone>
      <gmd:address>
        <gmd:CI_Address>
          <gmd:electronicMailAddress>
            <gco:CharacterString/>
          </gmd:electronicMailAddress>
        </gmd:CI_Address>
      </gmd:address>
      <gmd:onlineResource>
        <gmd:CI_OnlineResource>
          <gmd:linkage>
            <gmd:URL>http://www.mydomain.it</gmd:URL>
          </gmd:linkage>
        </gmd:CI_OnlineResource>
      </gmd:onlineResource>
    </gmd:CI_Contact>
  </gmd:contactInfo>
  <gmd:role>
    <gmd:CI_RoleCode codeList="./resource/codeList.xml#CI_RoleCode" codeListValue="owner">Proprietario</gmd:CI_RoleCode>
  </gmd:role>
</gmd:CI_ResponsibleParty>
</MD_Metadata>

My transformation:

<xsl:stylesheet
    version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:gml="http://www.opengis.net/gml/3.2"
    xmlns:gco="http://www.isotc211.org/schemas/2005/gco"
    xmlns:gmd="http://www.isotc211.org/schemas/2005/gmd"
    xmlns="http://www.isotc211.org/schemas/2005/gmd"
    >

    <xsl:strip-space elements="*"/>

    <xsl:output indent="yes" encoding="UTF-8"/>

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

    <xsl:template match="gmd:CI_ResponsibleParty" exclude-result-prefixes="#all">
        <xsl:copy>
            <!--<xsl:copy-of select="@*" />-->
            <xsl:copy-of select="gmd:organizationName" />
            <xsl:apply-templates select="gmd:contactInfo" />
            <xsl:copy-of select="gmd:role" />
            <!--<xsl:apply-templates select="node()" />-->
        </xsl:copy>
    </xsl:template>

    <!--<xsl:template match="gmd:contactInfo" />-->

    <xsl:template match="gmd:contactInfo" exclude-result-prefixes="#all">
        <xsl:copy>
            <xsl:copy-of select="gmd:phone" />
            <xsl:apply-templates select="gmd:address" />
            <xsl:if test="not(gmd:address)">
                <address>
                    <CI_Address>
                        <electronicMailAddress>
                            <gco:CharacterString/>
                        </electronicMailAddress>
                    </CI_Address>
                </address>
            </xsl:if>
            <xsl:copy-of select="gmd:onlineResource" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

标签: xml xslt
1条回答
Anthone
2楼-- · 2019-07-29 18:46

I think you want to use <xsl:template match="gmd:CI_contact">, not <xsl:template match="gmd:contactInfo">

<xsl:template match="gmd:CI_Contact">
    <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates select="gmd:phone" />
        <xsl:apply-templates select="gmd:address" />
        <xsl:if test="not(gmd:address)">
            <address>
                <CI_Address>
                    <electronicMailAddress>
                        <gco:CharacterString/>
                    </electronicMailAddress>
                </CI_Address>
            </address>
        </xsl:if>
        <xsl:apply-templates select="gmd:onlineResource" />
    </xsl:copy>
</xsl:template>

As a good practice: In stylesheets that are based on the identity template, prefer <xsl:apply-templates> over <xsl:copy>.

The end effect will be the same (the input will be copied) but this way you keep it easy to append another template that handles a special case while not having to touch the existing templates.

Hypothetically: Say you want to drop all @foo attributes everywhere. Because the above uses <xsl:apply-templates select="@*">, it's a matter of appending

<xsl:template match="@foo" />

However if you use <xsl:copy-of select="@*" /> you'd have to change this line and probably a dozen other places where @foo would be copied.

查看更多
登录 后发表回答