简体   繁体   中英

Sorting XMLTag using XSL Stylesheet Java Transformer Factory

I have below XML file that i would like to sort it in specific way. Basically XMLTags will be sorted alphabatically first and then within each XMLtag, one of the XMLelement will be used to sort them alphabetically also. Please see the current XML and the final result that i am required

Original XML

    <?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<applicationVisibilities>
    <application>Test</application>
    <default>true</default>
    <visible>true</visible>
</applicationVisibilities>
<classAccesses>
    <apexClass>TestClass</apexClass>
    <enabled>false</enabled>
</classAccesses>
<applicationVisibilities>
    <application>Class</application>
    <default>true</default>
    <visible>false</visible>
</applicationVisibilities>
<classAccesses>
    <apexClass>FooClass</apexClass>
    <enabled>false</enabled>
</classAccesses>
<fieldPermissions>
    <editable>false</editable>
    <field>Hello</field>
    <readable>true</readable>
</fieldPermissions>
<applicationVisibilities>
    <application>Foo</application>
    <default>true</default>
    <visible>false</visible>
</applicationVisibilities>
<fieldPermissions>
    <editable>false</editable>
    <field>Blah</field>
    <readable>true</readable>
</fieldPermissions>
</Profile>

Final Output

    <?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<applicationVisibilities>
    <application>Class</application>
    <default>true</default>
    <visible>false</visible>
</applicationVisibilities>
<applicationVisibilities>
    <application>Foo</application>
    <default>true</default>
    <visible>false</visible>
</applicationVisibilities>
<applicationVisibilities>
    <application>Test</application>
    <default>true</default>
    <visible>true</visible>
</applicationVisibilities>
 <classAccesses>
    <apexClass>FooClass</apexClass>
    <enabled>false</enabled>
</classAccesses>
<classAccesses>
    <apexClass>TestClass</apexClass>
    <enabled>false</enabled>
</classAccesses>
<fieldPermissions>
    <editable>false</editable>
    <field>Blah</field>
    <readable>true</readable>
</fieldPermissions>
<fieldPermissions>
    <editable>false</editable>
    <field>Hello</field>
    <readable>true</readable>
</fieldPermissions>
</Profile>

Once the tags are sorted then for each of those tags, i would use specific element to sort them. Example: applicaitonVisibilities tag will be sorted alphabetically using application xmlelement value. For classAccesses, apexClass will be used for sorting alphabatically and lastly for fieldPermissions, field element will be used for sorting. I was playing around with the current XSL stylesheet but it's not working.

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

<xsl:template match="Profile/applicationVisibilities">
    <xsl:copy>
        <xsl:apply-templates select="application">
            <xsl:sort select="node()" data-type="text" order="ascending" />
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

My current java code for using the current XSL

XSL   TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer(new StreamSource(new File("profile.xsl")));
    //transformerFactory.setAttribute("indent-number", 10);
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
    transformer.setOutputProperty(OutputKeys.METHOD, "xml");
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
    transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    DOMSource source = new DOMSource(document);
    StreamResult result = new StreamResult(new File(originalFile));
    transformer.transform(source, result);

In XSLT 3 with higher-order function support (Saxon 10 or later all editions, Saxon 9.8 and 9.9 PE and EE, Saxon-JS 2) you could use grouping on the node-name to then sort the groups based on a map from the name to a function selecting the child element:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
    exclude-result-prefixes="#all"
    version="3.0">
    
  <xsl:param name="sort-keys"
    as="map(xs:QName, function(*))"
    select="map {
              QName('http://soap.sforce.com/2006/04/metadata', 'applicationVisibilities') : function($el) { $el/application }, 
              QName('http://soap.sforce.com/2006/04/metadata', 'classAccesses') : function($el) { $el/apexClass },
              QName('http://soap.sforce.com/2006/04/metadata', 'fieldPermissions') : function($el) { $el/field } 
            }"/>

  <xsl:mode on-no-match="shallow-copy"/>

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

  <xsl:template match="/*">
      <xsl:copy>
          <xsl:for-each-group select="*" group-by="node-name()">
              <xsl:sort select="string(current-grouping-key())"/>
              <xsl:apply-templates select="current-group()">
                  <xsl:sort select="$sort-keys(node-name())(.)"/>
              </xsl:apply-templates>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

In earlier versions you could use

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

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

  <xsl:template match="/*">
      <xsl:copy>
          <xsl:for-each-group select="*" group-by="node-name()">
              <xsl:sort select="current-grouping-key() => string()"/>
              <xsl:apply-templates select="current-group()">
                  <xsl:sort select="if (. instance of element(applicationVisibilities))
                                    then application
                                    else if (. instance of element(classAccesses))
                                    then apexClass
                                    else if (. instance of element(fieldPermissions))
                                    then field
                                    else ()"/>
              </xsl:apply-templates>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

A (hopefully) pure XSLT 2 version of that would be

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
    exclude-result-prefixes="#all"
    version="2.0">

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

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

  <xsl:template match="/*">
      <xsl:copy>
          <xsl:for-each-group select="*" group-by="node-name(.)">
              <xsl:sort select="string(current-grouping-key())"/>
              <xsl:apply-templates select="current-group()">
                  <xsl:sort select="if (. instance of element(applicationVisibilities))
                                    then application
                                    else if (. instance of element(classAccesses))
                                    then apexClass
                                    else if (. instance of element(fieldPermissions))
                                    then field
                                    else ()"/>
              </xsl:apply-templates>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM