Creating and using attributes in XSLT - xslt

I'm trying to write xsl that would take something like this:
<Invoices>
<Invoice>
<Customer>
<Type>P</Type>
<First>John</First>
<Last>Doe</Last>
<Id>123-45</Id>
</Customer>
<Amount>1000.00</Amount>
</Invoice>
<Invoice>
<Customer>
<Type>C</Type>
<Name>ACME Inc</Name>
<Id>765-432</Id>
</Customer>
<Amount>3456.78</Amount>
</Invoice>
</Invoices>
and produce something like this:
<Customers>
<Customer type="Person" ref="1">
<FirstName>John</FirstName>
<LastName>Doe</Lastname>
<Id>123-45</Id>
</Customer>
<Customer type="Company" ref="2">
<Name>ACME Inc</Name>
<Id>765-432</Id>
</Customer>
<Customers>
<Invoices>
<Invoice>
<Recipient>1</Recipient>
<Amount>1000.00</Amount>
</Invoice>
<Invoice>
<Recipient>2</Recipient>
<Amount>3456.78</Amount>
</Invoice>
</Invoices>
I.e. the customers should be listed separately with each having an unique ref-attribute and then the invoices should use that attribute to reference the recipient. One of the things causing me problems is that the schema for the output defines the "ref"-attribute having type long, so I can't directly use the value of Id-element, which contains non-numeric characters. I can use e.g. position() to get unique reference, but how do I keep track of the references used?

Related

BizTalk XSLT Mapping For Grouping and Sorting with Multiple Records

Input Message :
The Input File Has Three Records inside the Detail is in the order “Member” , “Product” and “Dependent” , In each Record there is a common Field which is “ Identifier” For Some reason we getting like Each Member and Product are looped into one detail and Each dependent is looping into separate Detail
................................................................................
<ns0:Root xmlns:ns0="Test">
<Detail>
<Member>
<Name>Jerry</Name>
<Address>Miami</Address>
<PhoneNumber>7008084201</PhoneNumber>
<Identifier>225692067</Identifier>
</Member>
<Product>
<Name>Phone</Name>
<Type>Personal</Type>
<Serial>000000111111</Serial>
<Identifier>225692067</Identifier>
</Product>
</Detail>
<Detail>
<Dependent>
<DependentName>Tom</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
</Detail>
<Detail>
<Dependent>
<DependentName>Tom1</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>8228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
</Detail>
<Detail>
<Dependent>
<DependentName>Tom2</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>9228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
</Detail>
<Detail>
<Member>
<Name>John</Name>
<Address>Kansas</Address>
<PhoneNumber>5007684306</PhoneNumber>
<Identifier>699039521</Identifier>
</Member>
<Product>
<Name>Xbox</Name>
<Type>Personal</Type>
<Serial>000000222222</Serial>
<Identifier>699039521</Identifier>
</Product>
</Detail>
<Detail>
<Member>
<Name>Larry</Name>
<Address>Newjersey</Address>
<PhoneNumber>6004567307</PhoneNumber>
<Identifier>230903815</Identifier>
</Member>
<Product>
<Name>Iphone</Name>
<Type>Personal</Type>
<Serial>0000003333333</Serial>
<Identifier>230903815</Identifier>
</Product>
</Detail>
<Detail>
<Dependent>
<DependentName>Luis</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7897684302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
</Detail>
<Detail>
<Dependent>
<DependentName>LuisMead</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7229876302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
</Detail>
</ns0:Root>
Expected OutPut XML :
The OutPut File is also Similar to the Input File but the order is “Member” , “Dependent” and “Product” . The Common Field “Identifier” is the common on in this case also.The idea is to make Detail to loop on “Member” , “Dependent” and “Product” order.
......................................................................................
<ns0:Root xmlns:ns0="Test">
<Detail>
<Member>
<Name>Jerry</Name>
<Address>Miami</Address>
<PhoneNumber>7008084201</PhoneNumber>
<Identifier>225692067</Identifier>
</Member>
<Dependent>
<DependentName>Tom</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
<Dependent>
<DependentName>Tom1</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>8228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
<Dependent>
<DependentName>Tom2</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>9228084302</DependentPhoneNumber>
<Identifier>225692067</Identifier>
</Dependent>
<Product>
<Name>Phone</Name>
<Type>Personal</Type>
<Serial>000000111111</Serial>
<Identifier>225692067</Identifier>
</Product>
</Detail>
<Detail>
<Member>
<Name>John</Name>
<Address>Kansas</Address>
<PhoneNumber>5007684306</PhoneNumber>
<Identifier>699039521</Identifier>
</Member>
<Product>
<Name>Xbox</Name>
<Type>Personal</Type>
<Serial>000000222222</Serial>
<Identifier>699039521</Identifier>
</Product>
</Detail>
<Detail>
<Member>
<Name>Larry</Name>
<Address>Newjersey</Address>
<PhoneNumber>6004567307</PhoneNumber>
<Identifier>230903815</Identifier>
</Member>
<Dependent>
<DependentName>Luis</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7897684302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
<Dependent>
<DependentName>LuisMead</DependentName>
<DependentAddress>Miami</DependentAddress>
<DependentPhoneNumber>7229876302</DependentPhoneNumber>
<Identifier>230903815</Identifier>
</Dependent>
<Product>
<Name>Iphone</Name>
<Type>Personal</Type>
<Serial>0000003333333</Serial>
<Identifier>230903815</Identifier>
</Product>
</Detail>
</ns0:Root>
Need Suggestion on writing XSLT 1.0 Code for this.
You can do this in XSLT-1.0 with Muenchian Grouping. Search for it on StackOverflow and you will find a lot of examples. Applying it created the following answer:
Create an xsl:key with the nodes Member|Dependent|Product using its Identifier element value as a key
Create a sortingOrder variable which provides indices for sorting the entries in the inner xsl:for-each
Match and copy the root node /ns0:Root with a template
Loop over all children of the Detail elements in a xsl:for-each. The expression applies the Muenchian Grouping method
Create a Detail elements and loop over the results of the previous xsl:for-each sorted by the index of the occurrence of the name of the current element in the sortingOrder variable. Copy its content. The method of ordering the elements was taken from this SO answer: "Sorting XML in XSLT based on a list of values".
The stylesheet could look like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="Test">
<xsl:output indent="yes"/>
<xsl:key name="id" match="Member|Dependent|Product" use="Identifier" />
<xsl:variable name="sortingOrder" select="'Member,Dependent,Product'" />
<xsl:template match="/ns0:Root">
<xsl:copy>
<xsl:for-each select="Detail/*[generate-id() = generate-id(key('id',Identifier)[1])]">
<Detail>
<xsl:for-each select="key('id',Identifier)">
<xsl:sort data-type="number" select="string-length(substring-before($sortingOrder,local-name()))" />
<xsl:copy-of select="."/>
</xsl:for-each>
</Detail>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

Multiple child elements in XSL for each

Am using for each tag in XSL:
Scenario
Input xml
<root>
<parent>
<Child 1>
<Child2>
data1
data2
data3
....
...
</child2>
<Child2>
data1
data2
data3
....
...
</child2>
<Child2>
data1
data2
data3
....
...
</child2>
</child1>
</parent>
When I am using for each on parent tag, in transformation file it is taking only the first occurence of child 2 repeatedly .
My requirement is, it need to take all the child two elements of child one.
Pretty sure from the comments that you've not got well-formed input XML. You can't have 2 R3 (root) elements at the top level of the document. This could be a push in the right direction.
Which data are you actually hoping to return, and in what format?
You've also hamstrung yourself by not having a repeating group inside Adress.
If you have the option, I'd reform your XML document into something more useful, because currently it's close to unworkable.
Recommend:
<?xml version="1.0" encoding="UTF-8"?>
<AddressBook owner="" date="">
<Address>
<addr firstName="" secondName="">
</addr>
<addr firstName="" secondName="">
</addr>
<addr firstName="" secondName="">
</addr>
</Address>
<Address>
<addr firstName="" secondName="">
</addr>
<addr firstName="" secondName="">
</addr>
<addr firstName="" secondName="">
</addr>
</Address>
</AddressBook>
This way, you can iterate through the elements which are named the same.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/AddressBook">
<xsl:for-each select="Address">
<xsl:for-each select="addr">
<xsl:value-of select="concat(#firstName, ', ', #secondName)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Alternately, if you need to add address lines, make a repeating group inside your addr elements, and give them id's to sort by, to make sure they're in order.
Hope this points you in the right direction.

Transforming attribute values into xml element

Using xslt 1.0, I need to transform below input xml to output xml
--input xml
<Row>
<Column name="NUMBER" sqltype="int">123</Column>
<Column name="DEPT1" sqltype="int">A</Column>
<Column name="CUST_EMPTYPE" sqltype="int">1</Column>
<Column name="CUST_TIJD" sqltype="int">31</Column>
</Row>
--output xml
<EMPLOYEE xmlns="http://xmlns.oracle.com/Employee">
<NUMBER>123</NUMBER>
<DEPT1>IHC</DEPT1>
<CUST_EMPTYPE>1</LASTNAME>
<CUST_TIJD>31</FIRSTNAME>
</EMPLOYEE>
the Column names from input xml are not known at design time, the Column can grow..
Can anyone let me know how to achieve this?
Thank you very much,
Yes. It is quite simple. One minor difference is the deviation between your DEPT1 source and destination value (I really don't know where 'IHC' may have been coming from). I harmonized it in the following XSLT code which just sets the Column/#name nodes to new EMPLOYEE elements with the content of the old text() content:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />
<xsl:template match="/Row">
<EMPLOYEE xmlns="http:xmlns.oracle.com/Employee">
<xsl:for-each select="Column">
<xsl:element name="{#name}">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:for-each>
</EMPLOYEE>
</xsl:template>
</xsl:stylesheet>
The result of this is:
<EMPLOYEE xmlns="http:xmlns.oracle.com/Employee">
<NUMBER>123</NUMBER>
<DEPT1>A</DEPT1>
<CUST_EMPTYPE>1</CUST_EMPTYPE>
<CUST_TIJD>31</CUST_TIJD>
</EMPLOYEE>

XSL:IF within XSL:for-each-group

In my XSLT, I have something like:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="PhyscianTotals" name="PhyscianTotals">
<xsl:for-each select="PhysicianTotals">
<xsl:for-each-group select="Statistic" group-by="Type">
<xsl:if test="Title='PHYSICIAN DETAIL TOTAL'">
<xsl:element name="totals">
</xsl:element>
</xsl:if>
</xsl:for-each-group>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Is this valid XSLT? Specifically, the section of "xsl:if within the xsl:for-each-group". One of the XSLT compilation tool we call always error out stating: xsl:if is not allowed at this position in the stylesheet. If I remove the xsl:for-each-group, it passes. I am not sure if it's my xslt having errors or if it's the compilation tool.
Turns out our tool only support XSLT 1.0. So I guess I am back to rewrite the XSLT using 1.0 tags only.
The original XML looks like:
<?xml version="1.0" encoding="UTF-8"?>
<PhysicianTotals>
<Statistic>
<Title>PHYSICIAN TOTAL</Title>
<Type>Type 1</Type>
<Key>Cases</Key>
<Value>1</Value>
</Statistic>
<Statistic>
<Title>PHYSICIAN TOTAL</Title>
<Type>Type 1</Type>
<Key>Percentage</Key>
<Value>25.0%</Value>
</Statistic>
<Statistic>
<Title>PHYSICIAN TOTAL</Title>
<Type>Type 2</Type>
<Key>Cases</Key>
<Value>3</Value>
</Statistic>
<Statistic>
<Title>PHYSICIAN TOTAL</Title>
<Type>Type 1</Type>
<Key>Percentage</Key>
<Value>75.0%</Value>
</Statistic>
</PhysicianTotals>
And the output will look like:
<?xml version="1.0" encoding="UTF-8"?>
<totals>
<type>PHY_DETAIL</type>
<detailInfo>
<code>Type 1</code>
</detailInfo>
<count>
<caseValue>1</caseValue>
<percentValue>25.0%</percentValue>
</count>
</totals>
<totals>
<type>PHY_DETAIL</type>
<detailInfo>
<code>Type 2</code>
</detailInfo>
<count>
<caseValue>3</caseValue>
<percentValue>75.0%</percentValue>
</count>
</totals>
Apart from the copy/paste error in the xsl:output declaration, your code looks perfectly OK to me. It's a bit suspect - do you really have an element called PhyscianTotals with a child called PhysicianTotals - so I suspect you're not showing us the code that actually generates the error.
Another possibility is that the tool generating the error is an XSLT 1.0 processor.
enchttp://stackoverflow.com/editing-helpoding="UTF-8"
This is syntactically/lexically illegal name: enchttp://stackoverflow.com/editing-helpoding
Did you mean encoding?
Apart from this there are no other lexical/syntax errors, and it seems that the transformation has a number of logical issues and is likely not to produce the wanted result -- but I cannot be 100% certain without seeing the source XML document and the desired result.

calling function with xslt using xpath selection

How can I call javascript/c# function in xslt passing xpath selection value.
Here is how I call function with manualy typed parameter:
<xsl:value-of select="cs:my('some text')"/>
Here is an example from the MSXML 4 SDK (this should be the same for MSXML 6, and is quite similar for .NEt's XslCompiledTransform -- for the latter search MSDN for <msxsl:script>)
Example This example defines a script block with a namespace
prefix of user that contains a function called xml that takes a
node-list as an argument. Later, this function, xml(nodelist) in the
user namespace, is called from the select attribute of .
XML File (customers.xml)
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="script.xsl" ?>
<customers>
<customer>
<name>John Smith</name>
<address>123 Elm St.</address>
<phone>(123) 456-7890</phone>
</customer>
<customer>
<name>Mary Jones</name>
<address>456 Oak Ave.</address>
<phone>(156) 789-0123</phone>
</customer>
</customers>
XSLT File (script.xsl)
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://mycompany.com/mynamespace">
<msxsl:script language="JScript" implements-prefix="user">
function xml(nodelist) {
return nodelist.nextNode().xml;
}
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:xml(.)"/>
</xsl:template>
</xsl:stylesheet>
Formatted Output
<?xml version="1.0" encoding="UTF-16"?><?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="script.xsl" ?>
<customers>
<customer>
<name>John Smith</name>
<address>123 Elm St.</address>
<phone>(123) 456-7890</phone>
</customer>
<customer>
<name>Mary Jones</name>
<address>456 Oak Ave.</address>
<phone>(156) 789-0123</phone>
</customer>
</customers>