0

I have an input XML, which I need to transform using XSLT 2.0.

Here is my input xml :

<root>
         <person>
            <LastName>yyyyy</LastName>
            <FirstName>xxxx</FirstName>
            <profession>IT/xx</profession>
            <area>0000</area>
            <email>xxx.yyy@xxx.com</email>
            <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
            <sex>male</sex>
         </person>
      </root>

My input xml is missing some nodes. For example (mobile, country which are mandatory as per xsd). Now, I need to add those two nodes ( as empty nodes) and then I need to sort including those two nodes.

Sort order should be : FirstName,LastName,email,sex,profession,mobile,Address,area,country;

Final output xml should be :

<root>
         <person>
            <FirstName>xxxx</FirstName>
            <LastName>yyyyy</LastName>
            <email>xxx.yyy@xxx.com</email>
            <sex>male</sex>
            <profession>IT/xx</profession>
            <mobile/>
            <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
            <area>0000</area>
            <country/>
         </person>
      </root>

I tried with the below XSLT :

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pOrderedNames" select="'FirstName,LastName,email,sex,profession,mobile,Address,area,country'"/>

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

    <xsl:template match="person[not(./country)]" mode="country">
        <xsl:element name="country" />
    </xsl:template>

    <xsl:template match="person[not(./mobile)]" mode="mobile">
        <xsl:element name="mobile" />
    </xsl:template>

    <xsl:template match="*" mode="sorter">
        <xsl:copy>
            <xsl:apply-templates>
                <xsl:sort data-type="number" select="string-length(substring-before($pOrderedNames,concat(',',name(),',')))"/>  
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="person">
        <xsl:copy>
            <xsl:apply-templates select="node()"/>
            <xsl:apply-templates  select="self::node()" mode="mobile"/>
            <xsl:apply-templates  select="self::node()" mode="country"/>
            <xsl:apply-templates  select="self::node()" mode="email"/>
            <xsl:apply-templates  select="self::node()" mode="sex"/>    
            <xsl:apply-templates select="self::*" mode="sorter"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

And with the above XSLT it is getting transformed as below :

<root>
   <person>
      <LastName>yyyyy</LastName>
      <FirstName>xxxx</FirstName>
      <profession>IT/xx</profession>
      <area>0000</area>
      <email>xxx.yyy@xxx.com</email>
      <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
      <sex>male</sex>
      <mobile/>
      <country/>yyyyyxxxxIT/xx0000xxx.yyy@xxx.comaaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeeemaleyyyyyxxxxIT/xx0000xxx.yyy@xxx.comaaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeeemale<person>
         <FirstName>xxxx</FirstName>
         <LastName>yyyyy</LastName>
         <email>xxx.yyy@xxx.com</email>
         <sex>male</sex>
         <profession>IT/xx</profession>
         <Address>aaaaaaaaa,bbbbbbbbbbbb,cccccccccc,dddddddddd,eeeeeeee</Address>
         <area>0000</area>
      </person>
   </person>
</root>

Can someone help me out here, please.

Thanks in Advance.

krishh
  • 1
  • 4
  • Please show the efforts that you have taken to write the XSLT. This is a simple transformation and can be achieved by going through XSLT basics. – Aniket V Oct 06 '17 at 07:21
  • Hello Aniket, Thanks for your reply. I have included the XSLT that I tried with. The result I am getting is almost near by, but some duplicate data is coming in the output xml. Can you please have a look at it. – krishh Oct 06 '17 at 09:58
  • Hello Aniket, it worked when I don't have tag in the input xml, but if tag is there then it is not working. I added details below.Could you please help me. – krishh Oct 07 '17 at 00:27
  • I have not provided any answer. Please check the answer provided by @Martin Honnen and respond accordingly. – Aniket V Oct 07 '17 at 06:09

1 Answers1

0

Here is an adaption of your approach which creates the new elements with the templates and sorts them by wrapping the various apply-templates into a perform-sort:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">   

    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pOrderedNames"
        select="'FirstName,LastName,email,sex,profession,mobile,Address,area,country'"/>
    <xsl:param name="pNameOrder" select="tokenize($pOrderedNames, ',')"/>

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

    <xsl:template match="person" mode="country">
        <country/>
    </xsl:template>

    <xsl:template match="person" mode="mobile">
        <mobile/>
    </xsl:template>

    <xsl:template match="person">
        <xsl:copy>
            <xsl:perform-sort>
                <xsl:sort select="index-of($pNameOrder, local-name())"/>
                <xsl:apply-templates select="*"/>
                <xsl:apply-templates select=".[not(country)]" mode="country"/>
                <xsl:apply-templates select=".[not(mobile)]" mode="mobile"/>
            </xsl:perform-sort>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

I am not sure to create the new elements you need a template and a mode, a simple <xsl:if test="not(mobile)"><mobile/></xsl:if> would suffice, but using a mode is a possible way.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Hi Martin, Thank you for your reply. It worked when I don't have tag in the input xml, but if tag is there then it is not working. I added details below. Could you please help me. Thanks in Advance. – krishh Oct 07 '17 at 08:38
  • @krishh, I have edited the answer and changed to code to only apply-templates to the `person` element itself when the child element does not exist, that should avoid the problem you have encountered, It might be easier to simply create the new elements within an `xsl:if` test. – Martin Honnen Oct 07 '17 at 09:18