Selecting distinct values from XSLT sub query - xslt

I have this XML document:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<properties>
<property name="prop1" type="type1"/>
<property name="prop2" type="type2"/>
<property name="prop3" type="type3"/>
<property name="prop4" type="type1"/>
</properties>
<types>
<type name="type1" group="group1"/>
<type name="type2" group="group1"/>
<type name="type3" group="group2"/>
<type name="type4" group="group3"/>
</types>
<groups>
<group name="group1" owner="owner1"/>
<group name="group2" owner="owner2"/>
<group name="group3" owner="owner3"/>
</groups>
</metadata>
I am transforming it using this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="docRoot" select="/" />
<xsl:for-each select="distinct-values($docRoot/metadata/properties/property/#type)">
<xsl:variable name="groupOwner" select="$docRoot/metadata/groups/group[#name=$docRoot/metadata/types/type[#name=current()]/#group]/#owner" />
<xsl:value-of select="$groupOwner"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
What I want to do is print out the list of unique group owners for all of the properties in the document. I'm successfully filtering out duplicate types with distinct-values but can't see how to filter out duplicate owners.
The current output:
owner1
owner1
owner2
The required output:
owner1
owner2
If it helps no two groups have the same owner.

Keys can be your friend here...
<xsl:key name="types" match="type" use="#name" />
<xsl:key name="groups" match="group" use="#name" />
Then you can do this, without even any need for distinct-values because you won't get duplicate nodes returned this way:
<xsl:for-each select="key('groups', key('types', metadata/properties/property/#type)/#group)">
For example, try this XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="types" match="type" use="#name" />
<xsl:key name="groups" match="group" use="#name" />
<xsl:template match="/">
<xsl:for-each select="key('groups', key('types', metadata/properties/property/#type)/#group)">
<xsl:value-of select="concat(#owner, '
')" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
In fact, you can simplify the xsl:for-each to this:
<xsl:value-of select="key('groups', key('types', metadata/properties/property/#type)/#group)/#owner" separator="
" />

Ah, just needed to try a bit harder:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="docRoot" select="/" />
<xsl:for-each select="distinct-values($docRoot/metadata/groups/group[#name=$docRoot/metadata/types/type[#name=$docRoot/metadata/properties/property/#type]/#group]/#owner)">
<xsl:value-of select="current()"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Open to any suggestions for simplifying this though!

Related

Transform an XML by grouping the fields

My knowledge to XSLT is limited but always eager to learn. I am currently working on a template that requires to transform the XML input. I've been trying to group the InvoiceNum fields and not getting anywhere.
I am getting an error: Envision.Utilities.XsltEngine-Object reference not set to an instance of an object.
Here's the input XML for reference:
<?xml version='1.0' ?>
<Request>
<Information>
<ImageID>987456321</ImageID>
<Contract>123456789</Contract>
<Lastname>MICKEYMOUSE</Lastname>
</Information>
<Document>
<InvoiceNum>123456823</InvoiceNum>
<Reference>AD20985224</Reference>
<InvoiceNum>100000123</InvoiceNum>
<Reference>AS20101387</Reference>
<InvoiceNum>858511825</InvoiceNum>
<Reference>GF96844</Reference>
<InvoiceNum>885154145</InvoiceNum>
<Reference>FGFD2018</Reference>
<InvoiceNum>25241111</InvoiceNum>
<Reference>SD88888</Reference>
<InvoiceNum>8571414</InvoiceNum>
<Reference>DF864841254</Reference>
</Document>
</Request>
Here's my XSLT format for reference:
What am I missing? Is there better way to format the XSLT template I have currently below? Any help is greatly appreciated.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="cols" select="3" />
<xsl:template match="Request">
<table border="1">
<xsl:apply-templates select="InvoiceNum[position() mod $cols = 1]"/>
</table>
</xsl:template>
<xsl:template match="Document">
<xsl:variable name="group" select=". | following-sibling::InvoiceNum
[position() < $cols]" />
<xsl:for-each select="*">
<xsl:variable name="i" select="position()" />
<Invoice>
<InvoiceNumber>
<xsl:value-of select="InvoiceNum()"/>
</InvoiceNumber>
<xsl:for-each select="$group">
<xsl:value-of select="*[$i]"/>
</xsl:for-each>
</Invoice>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Here's the XML output I'd like to have:
<InvCase>
<Invoices>
<InvoicemNumber>InvoiceNum1</InvoicemNumber>
<InvoicemNumber>InvoiceNum2</InvoicemNumber>
<InvoicemNumber>InvoiceNum3</InvoicemNumber>
</Invoices>
</InvCase>
It looks like you are trying to group in the InvoiceNum into groups of 3. The first issue you have is that in your template matching Request you do this...
<xsl:apply-templates select="InvoiceNum[position() mod $cols = 1]"/>
But InvoiceNum is not a child of Request, and so that selects nothing. You probably need to do this...
Additionally, you have a template matching Document, but this probably needs to match InvoiceNum (Doing following-sibling::InvoiceNum would not return anything if you were matching Document as the InvoiceNum elements are children of Document not following siblings).
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="cols" select="3" />
<xsl:template match="Request">
<InvCases>
<xsl:apply-templates select="Document/InvoiceNum[position() mod $cols = 1]"/>
</InvCases>
</xsl:template>
<xsl:template match="InvoiceNum">
<xsl:variable name="group" select=". | following-sibling::InvoiceNum[position() < $cols]" />
<Invoice>
<xsl:for-each select="$group">
<InvoiceNumber>
<xsl:value-of select="."/>
</InvoiceNumber>
</xsl:for-each>
</Invoice>
</xsl:template>
</xsl:stylesheet>

XSL to get values from nodes basing on the attribute value match

I am pretty much new to xsl.Currently facing a challenge in transforming the below sample xml to required output.
Sample XML:
<root>
<types>
<entity-type>
<field-type identifier="FieldType1" proxy-ref="TypeA">
<object-name>aclProxy</object-name>
<persistence-class-name>java.lang.Long</persistence-class-name>
</field-type>
<field-type identifier="FieldType2" proxy-ref="TypeB">
<object-name>aclName</object-name>
<persistence-class-name>java.lang.String</persistence-class-name>
</field-type>
</entity-type>
</types>
<custom>
<category identifier="parent" order="1">
<name>%category.parent</name>
</category>
<category identifier="texts" order="2">
<name>%category.Texts</name>
</category>
<field identifier="ArticleID" category-ref="parent" field-type-ref="FieldType1" proxy-entity-ref="ABC">
<name>%field.name</name>
<description>%field.Article.description</description>
</field>
<field identifier="ArticleName" category-ref="texts" field-type-ref="FieldType2" proxy-entity-ref="ABCD">
<name>%field.name.text</name>
<description>%field.Articletext.description</description>
</field>
</custom>
</root>
Output
<field identifier="ArticleID">
<name>%field.name</name>
<description>%field.Article.description</description>
<category-ref-name>%category.parent</category-ref-name>
<field-type-ref>Long</field-type-ref>
<proxy-entity-ref>TypeA</proxy-entity-ref>
</field>
<field identifier="ArticleName" >
<name>%field.name.text</name>
<description>%field.Articletext.description</description>
<category-ref-name>%category.Texts</category-ref-name>
<field-type-ref>String</field-type-ref>
<proxy-entity-ref>TypeB</proxy-entity-ref>
</field>
XSLT which I have been trying:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="category" match="category" use="#identifier" />
<xsl:key name="field" match="field" use="#category-ref" />
<xsl:apply-templates select="custom[not(key('field',category/#identifier))]"/>
<xsl:template match="/">
<xsl:text>something</xsl:text>
<xsl:value-of select="category/#identifier"/>
<xsl:apply-templates select="."/>
</xsl:template>
<xsl:template match="/">
<xsl:value-of select="field/#identifier"/>
<xsl:variable name="categoryparam" select="key('category', field/#category-ref)" />
<xsl:if test="$categoryparam">
<xsl:apply-templates select="$categoryparam"/>
<xsl:text>something</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Basically the output should have only field(s) in it referring to the attributes and get the node values.
Any help is much appreciated.
Thanks in advance.
Let us start off with these 2 keys:
<xsl:key name="kCategory" match="category" use="#identifier" />
<xsl:key name="kFieldID" match="field-type" use="#identifier" />
you will need the values in category and field-type nodes later.
This template:
<xsl:template match="/">
<xsl:apply-templates select="root/custom/field"/>
</xsl:template>
will output the target field nodes
Having a template match with the field node to further manipuate the output:
<xsl:template match="field">
<xsl:copy>
<!-- copies the identifier attribute and all children -->
<xsl:copy-of select="#identifier|node()"/>
<category-ref-name>
<!-- gets the child name of the target category node
that matches the category-ref attribute -->
<xsl:value-of select="key('kCategory', #category-ref)/name"/>
</category-ref-name>
<field-type-ref>
<!-- gets the child persistence-class-name of the target
field-type node that matches the field-type-ref attribute,
with further substring manipulations -->
<xsl:value-of select="substring-after(key('kFieldID', #field-type-ref)/persistence-class-name, 'java.lang.')"/>
</field-type-ref>
<proxy-entity-ref>
<!-- gets the attribute proxy-ref of the target
field-type node that matches the field-type-ref
attribute -->
<xsl:value-of select="key('kFieldID', #field-type-ref)/#proxy-ref"/>
</proxy-entity-ref>
</xsl:copy>
</xsl:template>
The whole stylesheet is below:
<?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"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kCategory" match="category" use="#identifier" />
<xsl:key name="kFieldID" match="field-type" use="#identifier" />
<xsl:template match="/">
<xsl:apply-templates select="root/custom/field"/>
</xsl:template>
<xsl:template match="field">
<xsl:copy>
<xsl:copy-of select="#identifier|node()"/>
<category-ref-name>
<xsl:value-of select="key('kCategory', #category-ref)/name"/>
</category-ref-name>
<field-type-ref>
<xsl:value-of select="substring-after(key('kFieldID', #field-type-ref)/persistence-class-name, 'java.lang.')"/>
</field-type-ref>
<proxy-entity-ref>
<xsl:value-of select="key('kFieldID', #field-type-ref)/#proxy-ref"/>
</proxy-entity-ref>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it in action (http://xsltfiddle.liberty-development.net/gWmuiJc).

Facing Issue in XSLT Format

I am looking my xslt in below format:
<xml>
<apis>
<name>API Name</name>
<comment> Comment</comment>
<version>12</version>
</apis>
</xml>
XSLT Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:L7j="http://ns.l7tech.com/2012/08/jdbc-query-result" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xml>
<xsl:apply-templates select="//L7j:col" />
</xml>
</xsl:template>
<xsl:template match="//L7j:col">
<api>
<xsl:element name="{#name}">
<xsl:value-of select="." /></xsl:element>
</api>
</xsl:template>
</xsl:stylesheet>
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<L7j:jdbcQueryResult xmlns:L7j="http://ns.l7tech.com/2012/08/jdbc-query-result">
<L7j:row>
<L7j:col name="name" type="java.lang.String">Policy for service #0b8bab6913cc588557b6973e94d1bfdd, WSTrustSoapService</L7j:col>
<L7j:col name="comment">
<![CDATA[NULL]]>
</L7j:col>
<L7j:col name="version" type="java.lang.Integer">18</L7j:col>
</L7j:row>
<L7j:row>
<L7j:col name="name" type="java.lang.String">Policy for service #0b8bab6913cc588557b6973e94d5893d, UUPRStub</L7j:col>
<L7j:col name="comment">
<![CDATA[NULL]]>
</L7j:col>
<L7j:col name="version" type="java.lang.Integer">16</L7j:col>
</L7j:row>
</L7j:jdbcQueryResult>
If I understand correctly, you want to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:L7j="http://ns.l7tech.com/2012/08/jdbc-query-result"
exclude-result-prefixes="L7j">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/L7j:jdbcQueryResult">
<xml>
<xsl:apply-templates/>
</xml>
</xsl:template>
<xsl:template match="L7j:row">
<apis>
<xsl:apply-templates/>
</apis>
</xsl:template>
<xsl:template match="L7j:col">
<xsl:element name="{#name}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
</xsl:stylesheet>

xsl copy if attribute of current node equals to attribute of another node

I have a sample XML File:
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<MetaData>
<Ref MDID='ID'></Ref>
</MetaData>
<MetaData2>
<Ref MDID='ID2'></Ref>
</MetaData2>
<Items ID='ID'>
<Item OID='haveit'></Item>
<Item OID='ornot'></Item>
</Items>
<Items ID= ID2'>
<Item OID='ornot'></Item>
<Item OID='ornot'></Item>
</Items>
</XML>
I have to transform it so that I receive the following result.
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<MetaData>
<Ref MDID='ID'></Ref>
</MetaData>
<Items ID='ID'>
<Item OID='haveit'></Item>
</Items>
</XML>
So first I have to check if the Item 'haveit' exists. Then I copy the corresponding parent "Item". Then I need to copy the MetaData where the MDID equals to the Items ID (in this case 'ID', but I don't know the exact value in my real example)
What I have so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="Item" select = "'haveit'"/>
<xsl:template match="XML">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="Items">
<xsl:if test="child::Item[#OID = $Item]">
<xsl:copy>
<xsl:copy-of select="#*" /> <!-- copy attributes -->
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
With this code I'm able to copy the Item I want and the corresponding Items Element. Now I don't know how to get the right MetaData element. How can I check if the MDID of Ref hase the same value as the Items ID?
How about:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="oid" select="'haveit'"/>
<xsl:key name="meta" match="*" use="Ref/#MDID" />
<xsl:template match="/XML">
<xsl:variable name="matching-items" select="Items[Item/#OID=$oid]" />
<xsl:copy>
<xsl:copy-of select="key('meta', $matching-items/#ID)"/>
<xsl:apply-templates select="$matching-items"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Items">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:copy-of select="Item[#OID=$oid]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Convert string value as XML tag name

Below is my requirement. Can we do this using XSLT? I want to convert value of AttributeName as tag under policy and corresponding AttributeValue as value.
Input :
<Policy>
<Attributes>
<AttributeName>is_policy_loan</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_owners</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_twoyears</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
</Policy>
Output :
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>
The following xsl file will do the job:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- create the <AttributeName>AttributeValue</..> nodes -->
<xsl:template match="//Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
<!-- wrap nodes in a `Policy` node -->
<xsl:template match="/">
<Policy>
<xsl:apply-templates/>
</Policy>
</xsl:template>
</xsl:stylesheet>
The way i would do,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="Policy">
<xsl:element name="Policy">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output will be,
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>