XSLT - Filtering - xslt

I have a repeating xml tree like this -
<xml>
<head>this is a sample xml file</head>
<item><color>yellow</color><color>red</color></item>
<item><color>blue</color></item>
<item><color>grey</color><color>red</color><color>blue</color></item>
</xml>
As you can see, each item can have a varying number of color tags.
I wish to get all the color tags for the first two items only.

<xsl:template match="xml">
<xsl:apply-templates select="item[position() < 3]/color" />
</xsl:template>
<xsl:template match="color">
<xsl:copy-of select="." />
</xsl:template>
Applied to your XML this yields:
<color>yellow</color>
<color>red</color>
<color>blue</color>

One potential possible way to get the items which is technically perfectly correct and in no way makes assumptions about the structure of your document with respect to namespacing, future requirements or template construction is just a simple:
/xml/item[position() < 3]/color

Try this...
/xml/item[ position() < 3 ]/color

Add an ordinal field to each item and select the first two.

Related

Split xml to several output files

Simplistically, I have xml that contains 120 nodes. How can I create 3 xml files that have 50 nodes in each? I've marked output as dynamic. Then I've tried to apply auto-number function but I can't get when it fires and how to create condition on it. What I need is sthm like trigger that would cause creation of new file. My strategy:
P.S. I'm noob at MapForce.
Assuming your input is
<root>
<elt>...</elt>
...
</root>
then simplistically, you could do something like:
<xsl:template match="/">
<xsl:document href="1-50.xml">
<root>
<xsl:for-each select="root/elt[pos() <= 50]">
<xsl:copy-of select="."/>
</xsl:for-each>
</root>
</xsl:document>
<xsl:document href="51-100.xml">
<root>
<xsl:for-each select="root/elt[pos() >= 51 and pos() <= 100]">
<xsl:copy-of select="."/>
</xsl:for-each>
</root>
</xsl:document>
<!-- repeat for other portions of input -->
</xsl:template>
In practice, you'd want to be a little bit smarter to handle arbitrary numbers of nodes in the input.

Denormalize a document using XSLT

I am new to XSLT so excuse me if this is a noob question.
Let's say I have this XML document (one hotel element with 2 private_rates):
<hotel>
<private_rates>
<private_rate>
<id>1</id>
</private_rate>
<private_rate>
<id>2</id>
</private_rate>
</private_rates>
</hotel>
Is there any way to use XSLT to transform it into 2 hotel elements, each with one private rate ?
<hotel>
<private_rates>
<private_rate>
<id>1</id>
</private_rate>
</private_rates>
</hotel>
<hotel>
<private_rates>
<private_rate>
<id>2</id>
</private_rate>
</private_rates>
</hotel>
How would the XSLT for that look like? Any help will be greatly appreciated! thanks.
As an alternative to Jollymorphic's solution, my preference would be
<xsl:template match="private_rates">
<hotel>
<xsl:copy-of select="."/>
</hotel>
</xsl:template>
Since a legal XML document has to have a single, containing document element, I presume that this sequence of hotels is going inside something. That being said, how about this:
<xsl:template match="hotel">
<xsl:for-each select="private_rates/private_rate">
<hotel>
<private_rates>
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</private_rates>
</hotel>
</xsl:for-each>
</xsl:template>
The apply-templates usage here presumes that you also have an identity template in your stylesheet that will copy source content that isn't more specifically matched by other templates (such as this one).
Underscores are frowned upon in element and attribute names, incidentally.

How to map 2 different nodes into a repeating node using BizTalk mapper

I have this schema:
<Root>
<Customers>
<Customer>
<ID>123</ID>
<Name>John</Name>
</Customer>
</Customers>
<Order>
<ID>ABC</ID>
<Title>Boat</Title>
</Order>
</Root>
I need to map the two different records into one repeating record like this:
<Root>
<Data>
<ID>123</ID>
<Text>John</Text>
</Data>
<Data>
<ID>ABC</ID>
<Text>Boat</Text>
</Data>
</Root>
I tried to create two table loopings (one for Customer, one for Order) and got:
<Root>
<Data>
<ID>ABC</ID>
<Text>Boat</Text>
</Data>
</Root>
Tried one table looping with two rows, and got the same. (Tried also with the Gated option to check for existance which made no difference)
In reality the schemas are huge, the map is super complex (not built by me), has a lot of functoids, and many wires. So I would like to avoid creating a custom XSL, which will be easier for this task, but harder to maintain. This is the only part I need to change.
Anybody ?
Thanks.
For complex mapping, using a custom XSLT almost always ends up being simpler and more maintainable than the spider-web we often find in BizTalk maps. However, as you stated, you need to avoid re-coding the complete map, as you are only changing a small section.
You should be able to use the 'Inline XSLT Call Template' script type in the Scripting Functoid to combine the best of BizTalk maps and custom XSLT.
Extending from Sean B. Durkin's answer, you will need to set up 2 Call Template functoids, the first one wired to your output 'Data' node
<xsl:template name="DataTemplate">
<xsl:apply-templates select="//*[local-name()='Customer']|//*[local-name()='Order']" />
</xsl:template>
Your second Call Template will output the relevant data into the current output 'Data' node. Note, this second Functoid does not need to be wired to any node in your output document.
<xsl:template match="*[local-name()='Customer']|*[local-name()='Order']">
<xsl:element name="Data">
<xsl:element name="ID">
<xsl:value-of select="*[local-name()='ID']"/>
</xsl:element>
<xsl:element name="Text">
<xsl:value-of select="*[local-name()='Name']|*[local-name()='Title']" />
</xsl:element>
</xsl:element>
</xsl:template>
No need to use XSLT here. Simply drag a Looping functoid on the map. Connect both the Customer and the Order record as inputs to the functoid (yes, you can have multiple inputs). Connect the output of the functoid to the Data record. Then connect your fields directly (ID --> ID, Name --> Text). This will work.
The individual input records to a Looping functoid don't have to be repeating records in themselves. By connecting multiple inputs to the functoid, you are looping over the collection of instances.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Root>
<xsl:apply-templates select="*/Customers/Customer|*/Order"/>
</Root>
</xsl:template>
<xsl:template match="Customer|Order">
<Data>
<ID><xsl:value-of select="ID" /></ID>
<Text><xsl:value-of select="Name|Title" /></Text>
</Data>
</xsl:template>
</xsl:stylesheet>

XSLT: Using a different way of processing within the current way of processing

Below I'm trying to match certain nodes.
<xsl:template match="nodes">
<element>
<xsl:apply-templates select="nodes" mode="different" />
</element>
</xsl:template>
Now, there are multiple ways of processing for the same nodes. I want to use this different way of processing within the current way of processing. That's why I perform apply-templates on the same selection, which is nodes, however the mode is different now.
Here's how the different mode could look like:
<xsl:template match="nodes" mode="different">
<!-- another way of processing these nodes -->
</xsl:template>
Now, this does not work. Only the first type of processing is processed and the apply-templates call is simply not applied.
To be a bit more specific:
<xsl:template match="Foundation.Core.Association.connection">
<xsl:for-each select="Foundation.Core.AssociationEnd">
<someElement>
<xsl:apply-templates select="Foundation.Core.Association.connection" mode="different" />
</someElement>
</xsl:for-each>
</xsl:template>
As you can see, I select Foundation.Core.Association.connection. Of course this is wrong, but how do I refer to this element given the current element and position? Given Derek his comment, that should do it.
What am I doing wrong, how can I get what I want using XSLT? What could be another approach to solve this problem?
Thanks.
if "nodes" is referring to the same exact set of nodes in the containing match, try:
<xsl:template match="nodes">
<element>
<xsl:apply-templates select="." mode="different" />
</element>
</xsl:template>
<xsl:template match="Foundation.Core.Association.connection">
<xsl:for-each select="Foundation.Core.AssociationEnd">
<someElement>
<xsl:apply-templates
select="Foundation.Core.Association.connection"
mode="different" />
As you can see, I select
Foundation.Core.Association.connection.
Of course this is wrong, but how do I
refer to this element given the
current element and position?
Use:
<xsl:apply-templates select=".." mode="different" />
The element you want to process differently is the parent of the current node.
Of course, most likely this convoluted processing is not necessary at all, which would be confirmed, had you been able to show more of the XML document and to formulate the problem in a more succint way.

How to select these elements with Xpath?

I have a document, something like this:
<root>
<A node="1"/>
<B node="2"/>
<A node="3"/>
<A node="4"/>
<B node="5"/>
<B node="6"/>
<A node="7"/>
<A node="8"/>
<B node="9"/>
</root>
Using xpath, How can I select all B elements that consecutively follow a given A element?
It's something like following-silbing::B, except I want them to be only the immediately following elements.
If I am on A (node==1), then I want to select node 2.
If I am on A (node==3), then I want to select nothing.
If I am on A (node==4), then I want to select 5 and 6.
Can I do this in xpath? EDIT: It is within an XSL stylesheet select statement.
EDIT2: I don't want to use the node attribute on the various elements as a unique identifier. I included the node attribute only for purposes of illustrating my point. In the actual XML doc, I don't have an attribute that I use as a unique identifer. The xpath "following-sibling::UL[preceding-sibling::LI[1]/#node = current()/#node]"
keys on the node attribute, and that's not what I want.
Short answer (assuming current() is ok, since this is tagged xslt):
following-sibling::B[preceding-sibling::A[1]/#node = current()/#node]
Example stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:apply-templates select="/root/A"/>
</xsl:template>
<xsl:template match="A">
<div>A: <xsl:value-of select="#node"/></div>
<xsl:apply-templates select="following-sibling::B[preceding-sibling::A[1]/#node = current()/#node]"/>
</xsl:template>
<xsl:template match="B">
<div>B: <xsl:value-of select="#node"/></div>
</xsl:template>
</xsl:stylesheet>
Good luck!
While #Chris Nielsen's answer is the right approach, it leaves an uncertainty in cases where the compared attribute is not unique. The more correct way of solving this is:
following-sibling::B[
generate-id(preceding-sibling::A[1]) = generate-id(current())
]
This makes sure that the preceding-sibling::A is identical to the current A, instead of just comparing some attribute values. Unless you have attributes that are guaranteed to be unique, this is the only safe way.
A solution might be to first gather up all the following nodes using following-sibling::*, grab the first of these and require it to be a 'B' node.
following-sibling::*[position()=1][name()='B']