XSLT can't get xml child elements - c#

I have the following xsl stylesheet and xml but I can't get the elements in the xml parent node in xsl template match is not working, I can't get table and costumer nodes from xml
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<HTML>
<HEAD>
<TITLE>Title</TITLE>
</HEAD>
<BODY>
<xsl:apply-templates/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="/parent">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="costumer">
<p>Costumer</p>
</xsl:template>
<xsl:template match="table">
<xsl:variable name="name" select="#name"/>
<xsl:variable name="type" select="#type"/>
<xsl:variable name="height" select="#height"/>
<xsl:variable name="width" select="#wdth"/>
<xsl:variable name="margin-top" select="#margin-top"/>
<xsl:variable name="margin-left" select="#margin-left"/>
<table id="{$name}" width="{$width}" height="{$height}" style="margin-top:{$margin-top}; margin-left:{$margin-left}">
<thead>
<tr>
<xsl:apply-templates select="*[1]/*" mode="th"/>
</tr>
</thead>
<tbody>
<xsl:apply-templates select="*"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="/*/*/*" mode="th">
<th>
<xsl:value-of select="*"/>
</th>
</xsl:template>
<xsl:template match="/*/*">
<tr>
<xsl:apply-templates select="*"/>
</tr>
</xsl:template>
<xsl:template match="/*/*/*">
<xsl:variable name="texttd" select="#text"/>
<td>
<xsl:value-of select="$texttd"/>
</td>
</xsl:template>
</xsl:stylesheet>
And below is xml file
<?xml version="1.0" encoding="utf-8"?>
<parent>
<table name="region1" type="td" wdth="0" height="0" margin-top="1" margin-left="122">
<td margin-top="0" margin-left="152" width="36" height="13" font-family="Arial-BoldMT" font-size="16" font-weight="0" text="??"?" line-height="300" is-visible="True" color="#0A4462" />
<Text name="region1" type="Title" wdth="0" height="0" margin-top="7" margin-left="138">
<Title margin-top="0" margin-left="14" width="36" height="13" font-family="Arial-BoldMT" font-size="16" font-weight="0" text="??"?" line-height="300" is-visible="True" color="#0A4462" />
</Text>
</table>
<table name="region1" type="td" wdth="0" height="0" margin-top="1" margin-left="122">
<td margin-top="0" margin-left="152" width="36" height="13" font-family="Arial-BoldMT" font-size="16" font-weight="0" text="??"?" line-height="300" is-visible="True" color="#0A4462" />
<Text name="region1" type="Title" wdth="0" height="0" margin-top="7" margin-left="138">
<Title margin-top="0" margin-left="14" width="36" height="13" font-family="Arial-BoldMT" font-size="16" font-weight="0" text="??"?" line-height="300" is-visible="True" color="#0A4462" />
</Text>
</table>
<table name="region1" type="td" wdth="0" height="0" margin-top="1" margin-left="122">
<td margin-top="0" margin-left="152" width="36" height="13" font-family="Arial-BoldMT" font-size="16" font-weight="0" text="??"?" line-height="300" is-visible="True" color="#0A4462" />
<Text name="region1" type="Title" wdth="0" height="0" margin-top="7" margin-left="138">
<Title margin-top="0" margin-left="14" width="36" height="13" font-family="Arial-BoldMT" font-size="16" font-weight="0" text="??"?" line-height="300" is-visible="True" color="#0A4462" />
</Text>
</table>
<costumer></costumer>
<costumer></costumer>
</parent>

The generic templates like <xsl:template match="/*/*/*" > get a higher priority. It seems the number of slashes / in an expression count towards the priority.
I think you should increase the priority of the templates you want by making the XPath more specific
<xsl:template match="/parent/table">
<xsl:template match="/parent/costumer">
This will still not work for the costumer, so you need to move it to the end of the stylesheet, because items at the end are preferred in case of equal priorities.
You could also reduce the priority on the generic items explicitly like this:
<xsl:template match="/*/*/*" priority="-1">
As an alternative you can increase the priority of the items you want explicitly:
<xsl:template match="table" priority="5">
Other that that I would need the full expected output to see if that all helps building the solution you're looking for.
BTW: I guess you want customer instead of costumer.

Related

XSLT and recursion to generate table

I'm new to XSLT and am having some problems trying to format an XML document which has recursive nodes.
There have 2 styles of tree node which are group and data.
The problem is my current XSLT template unable to generate the content when the Nodes have mixed of group and data styles.
XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="Nodes[TreeNode]">
<xsl:apply-templates select="TreeNode" />
</xsl:template>
<xsl:template match="Nodes[not(TreeNode)]" />
<xsl:template match="TreeNode[Style='Data']">
<!--<table>
<thead>
<tr>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>-->
<tr>
<td>
<xsl:value-of select="Value"/>
</td>
</tr>
<!--</tbody>
</table>-->
</xsl:template>
<xsl:template match="TreeNode[Style='Group']">
<group>
<p>
<xsl:value-of select="Label"/>
</p>
<xsl:apply-templates select="Nodes" />
</group>
</xsl:template>
</xsl:stylesheet>
XML
<?xml version="1.0" encoding="utf-8"?>
<TreeNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Label>Root</Label>
<Style>Group</Style>
<Nodes>
<TreeNode>
<Label>A</Label>
<Style>Group</Style>
<Nodes>
<TreeNode>
<Label>B</Label>
<Style>Group</Style>
<Nodes>
<TreeNode>
<Label />
<Value>AAA</Value>
<Style>Data</Style>
<Nodes />
</TreeNode>
<TreeNode>
<Label />
<Value>BBB</Value>
<Style>Data</Style>
<Nodes />
</TreeNode>
</Nodes>
</TreeNode>
<TreeNode>
<Label>C</Label>
<Style>Group</Style>
<Nodes>
<TreeNode>
<Label />
<Value>CCC</Value>
<Style>Data</Style>
<Nodes />
</TreeNode>
<TreeNode>
<Label />
<Value>DDD</Value>
<Style>Data</Style>
<Nodes />
</TreeNode>
</Nodes>
</TreeNode>
<TreeNode>
<Label>D</Label>
<Style>Group</Style>
<Nodes>
<TreeNode>
<Label />
<Value>EEE</Value>
<Style>Data</Style>
<Nodes />
</TreeNode>
</Nodes>
</TreeNode>
</Nodes>
</TreeNode>
</Nodes>
</TreeNode>
Expected Result:
You have two templates at the start of your XSLT
<xsl:template match="Nodes[TreeNode]">
<xsl:apply-templates select="TreeNode" />
</xsl:template>
<xsl:template match="Nodes[not(TreeNode)]" />
These could actually be merged into one; like so:
<xsl:template match="Nodes">
<xsl:apply-templates select="TreeNode" />
</xsl:template>
The reason being is that if Nodes does not have a TreeNode under it, then <xsl:apply-templates select="TreeNode" /> will not select anything anyway, so the effect is the same. (In fact, you could drop this template entirely if Nodes could only ever have TreeNode under it, as XSLT's built-in templates will do the same thing).
However, in answer to your problem, is that what I think you need is another template that you need another template that matches Nodes in the case where there is a child TreeNode for "Data"
<xsl:template match="Nodes[TreeNode/Style='Data']">
<table>
<xsl:apply-templates select="TreeNode" />
</table>
</xsl:template>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="Nodes[TreeNode/Style='Data']">
<table>
<xsl:apply-templates select="TreeNode" />
</table>
</xsl:template>
<xsl:template match="Nodes">
<xsl:apply-templates select="TreeNode" />
</xsl:template>
<xsl:template match="TreeNode[Style='Data']">
<tr>
<td>
<xsl:value-of select="Value"/>
</td>
</tr>
</xsl:template>
<xsl:template match="TreeNode[Style='Group']">
<group>
<p>
<xsl:value-of select="Label"/>
</p>
<xsl:apply-templates select="Nodes" />
</group>
</xsl:template>
</xsl:stylesheet>

How to add nested context menu - visual studio addin

I created the extension for Visual Studio 2015 which looks this
But i want to place all the four menus in a category say My Group. Which should like this.
My Group [On click of this the rest of submenus shoud come same as shown in the image]
menu one
menu two
My vsct file looks like this
<Commands package="package">
<Groups>
<Group guid="PackageCmdSet" id="MenuGroup" priority="0x0300">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/>
</Group>
<Group guid="ClassPackageCmdSet" id="ProjectMenuGroup" priority="0x0400">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE"/>
</Group>
</Groups>
and i have buttons like this
<Buttons>
<Button guid="PackageCmdSet" id="BranchModelClassId" priority="0x0100" type="Button" >
<Parent guid="PackageCmdSet" id="MenuGroup" />
<Icon guid="guidImages" id="bmpPic1" />
<CommandFlag>DynamicVisibility</CommandFlag>
<Strings>
<CommandName>Test</CommandName>
<ButtonText>Test</ButtonText>
</Strings>
</Button>
How to created a nested menu which i shouwn in the second image ?
Please help me on this.
I finally figured out the way. The change has to be done in the vsct file.
Add menus first inside the Commands
<Menus>
<Menu guid="PackageCmdSet" id="MainMenu" priority="0x0100" type="Menu">
<Parent guid="PackageCmdSet" id="MenuGroup" />
<Strings>
<ButtonText>Nested Menu</ButtonText>
</Strings>
</Menu>
<Menus>
Then add groups
<Groups>
<Group guid="PackageCmdSet" id="MenuGroup" priority="0x0200">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE"/>
</Group>
<Group guid="PackageCmdSet" id="CommandsMenuGruop" priority="0x0300">
<Parent guid="PackageCmdSet" id="MainMenu"/>
</Group>
<Groups>
The button should be like this
<Buttons>
<Button guid="PackageCmdSet" id="ClassId" priority="0x0100" type="Button">
<Parent guid="PackageCmdSet" id="CommandsMenuGruop" />
<Icon guid="guidImages" id="bmpPic1" />
<CommandFlag>DynamicVisibility</CommandFlag>
<Strings>
<CommandName>Command</CommandName>
<ButtonText>Item 1</ButtonText>
</Strings>
</Button>
<Buttons>

Deserializing XML (TFS oData)

I am developping a windows 8.1 application and since few days I am struggling with a problem.
Here is my probem:
I have a tfs project and via my application I want to follow it (displaying the state, created by, changed by,etc) using tfs odata.
Here is the xml file obtained :
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="https://tfsodata.visualstudio.com/DefaultCollection/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<title type="text">WorkItems</title>
<id>https://tfsodata.visualstudio.com/DefaultCollection/Projects('project1')/WorkItems/</id>
<updated>2014-05-02T14:52:17Z</updated>
<link rel="self" title="WorkItems" href="WorkItems" />
<entry m:etag="W/"datetime'2014-03-12T19%3A54%3A38.193%2B00%3A00'"">
<id>https://tfsodata.visualstudio.com/DefaultCollection/WorkItems(1)</id>
<title type="text">ProjetTest</title>
<summary type="text"></summary>
<updated>2014-03-12T19:54:38Z</updated>
<author>
<name />
</author>
<link rel="edit" title="WorkItem" href="WorkItems(1)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Attachments" type="application/atom+xml;type=feed" title="Attachments" href="WorkItems(1)/Attachments" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Links" type="application/atom+xml;type=feed" title="Links" href="WorkItems(1)/Links" />
<category term="Microsoft.Samples.DPE.ODataTFS.Model.Entities.WorkItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:Id m:type="Edm.Int32">1</d:Id>
<d:Project>project1</d:Project>
<d:Type>Product Backlog Item</d:Type>
<d:WebEditorUrl>https://xxxx.visualstudio.com/web/wi.aspx?pcguid=f2ba9200-f167-43e8-a92e-d36b1bc1b561&id=1</d:WebEditorUrl>
<d:AreaPath>ptoject1</d:AreaPath>
<d:IterationPath>project1</d:IterationPath>
<d:Revision m:type="Edm.Int32">2</d:Revision>
<d:Priority m:null="true" />
<d:Severity m:null="true" />
<d:StackRank m:type="Edm.Double">0</d:StackRank>
<d:AssignedTo></d:AssignedTo>
<d:CreatedDate m:type="Edm.DateTime">2014-03-12T19:54:25.783+00:00</d:CreatedDate>
<d:CreatedBy>xxxxx</d:CreatedBy>
<d:ChangedDate m:type="Edm.DateTime">2014-03-12T19:54:38.193+00:00</d:ChangedDate>
<d:ChangedBy>xxxxx</d:ChangedBy>
<d:ResolvedBy m:null="true" />
<d:Title>ProjetTest</d:Title>
<d:State>New</d:State>
<d:Reason>New backlog item</d:Reason>
<d:CompletedWork m:type="Edm.Double">0</d:CompletedWork>
<d:RemainingWork m:type="Edm.Double">0</d:RemainingWork>
<d:Description></d:Description>
<d:ReproSteps m:null="true" />
<d:FoundInBuild m:null="true" />
<d:IntegratedInBuild></d:IntegratedInBuild>
<d:AttachedFileCount m:type="Edm.Int32">0</d:AttachedFileCount>
<d:HyperLinkCount m:type="Edm.Int32">0</d:HyperLinkCount>
<d:RelatedLinkCount m:type="Edm.Int32">0</d:RelatedLinkCount>
<d:Risk m:null="true" />
<d:StoryPoints m:type="Edm.Double">0</d:StoryPoints>
<d:OriginalEstimate m:type="Edm.Double">0</d:OriginalEstimate>
<d:BacklogPriority m:type="Edm.Double">1000000000</d:BacklogPriority>
<d:BusinessValue m:type="Edm.Int32">0</d:BusinessValue>
<d:Effort m:type="Edm.Double">0</d:Effort>
<d:Blocked m:null="true" />
<d:Size m:type="Edm.Double">0</d:Size>
</m:properties>
</content>
</entry>
<entry m:etag="W/"datetime'2014-03-24T12%3A07%3A56.397%2B00%3A00'"">
<id>https://tfsodata.visualstudio.com/DefaultCollection/WorkItems(2)</id>
<title type="text">test2</title>
<summary type="text"></summary>
<updated>2014-03-24T12:07:56Z</updated>
<author>
<name />
</author>
<link rel="edit" title="WorkItem" href="WorkItems(2)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Attachments" type="application/atom+xml;type=feed" title="Attachments" href="WorkItems(2)/Attachments" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Links" type="application/atom+xml;type=feed" title="Links" href="WorkItems(2)/Links" />
<category term="Microsoft.Samples.DPE.ODataTFS.Model.Entities.WorkItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:Id m:type="Edm.Int32">2</d:Id>
<d:Project>project1</d:Project>
<d:Type>Product Backlog Item</d:Type>
<d:WebEditorUrl>https://xxxxx.visualstudio.com/web/wi.aspx?pcguid=f2ba9200-f167-43e8-a92e-d36b1bc1b561&id=2</d:WebEditorUrl>
<d:AreaPath>project1</d:AreaPath>
<d:IterationPath>project1</d:IterationPath>
<d:Revision m:type="Edm.Int32">4</d:Revision>
<d:Priority m:null="true" />
<d:Severity m:null="true" />
<d:StackRank m:type="Edm.Double">0</d:StackRank>
<d:AssignedTo></d:AssignedTo>
<d:CreatedDate m:type="Edm.DateTime">2014-03-12T20:16:49.827+00:00</d:CreatedDate>
<d:CreatedBy>xxxx</d:CreatedBy>
<d:ChangedDate m:type="Edm.DateTime">2014-03-24T12:07:56.397+00:00</d:ChangedDate>
<d:ChangedBy>xxxx</d:ChangedBy>
<d:ResolvedBy m:null="true" />
<d:Title>test2</d:Title>
<d:State>Committed</d:State>
<d:Reason>Additional work found</d:Reason>
<d:CompletedWork m:type="Edm.Double">0</d:CompletedWork>
<d:RemainingWork m:type="Edm.Double">0</d:RemainingWork>
<d:Description></d:Description>
<d:ReproSteps m:null="true" />
<d:FoundInBuild m:null="true" />
<d:IntegratedInBuild></d:IntegratedInBuild>
<d:AttachedFileCount m:type="Edm.Int32">0</d:AttachedFileCount>
<d:HyperLinkCount m:type="Edm.Int32">0</d:HyperLinkCount>
<d:RelatedLinkCount m:type="Edm.Int32">0</d:RelatedLinkCount>
<d:Risk m:null="true" />
<d:StoryPoints m:type="Edm.Double">0</d:StoryPoints>
<d:OriginalEstimate m:type="Edm.Double">0</d:OriginalEstimate>
<d:BacklogPriority m:type="Edm.Double">999968378</d:BacklogPriority>
<d:BusinessValue m:type="Edm.Int32">0</d:BusinessValue>
<d:Effort m:type="Edm.Double">0</d:Effort>
<d:Blocked m:null="true" />
<d:Size m:type="Edm.Double">0</d:Size>
</m:properties>
</content>
</entry>
</feed>
my class:
paste special as xml classes (from the xml above)
my function :
public IEnumerable<TfsEntitiesXml.feed> Deserialize()
{
string xml = "https://tfsodata.visualstudio.com/DefaultCollection/Projects('xxxx')/WorkItems".Trim();
XmlSerializer serilaizer = new XmlSerializer(typeof(TfsEntitiesXml.feed));
//string xml = "";
byte[] buffer = Encoding.UTF8.GetBytes(xml);
var stream = new MemoryStream();
stream.WriteAsync(buffer, 0, buffer.Length);
IEnumerable<TfsEntitiesXml.feed> result = (IEnumerable<TfsEntitiesXml.feed>)serilaizer.Deserialize(stream);
return result;
}
and the xaml
<ListView x:Name="itemsListView"
SelectionMode="None"
ItemsSource="{Binding TfsList}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
Foreground=
"{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Style="{StaticResource TitleTextStyle}" Height="60"
TextWrapping="Wrap"
Margin="15,5,15,0">
<Run Text="{Binding Title}" ></Run>
<Run Text="{Binding State}" ></Run>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
var projects = tfsConnector.Deserialize();
DefaultViewModel["TfsList"] = projects;
When I run I get this exception
An exception of type 'System.InvalidOperationException' occurred in System.Xml.dll but was not handled in user code
Additional information: There is an error in XML document (0, 0).
I am struggling with this problem since few days now.
Can someone help me please?
Thank you
You should simply make a service reference to the tfsodata service, this way you will get a typed reference and don't have to mess with the xml.
See this blogpost

Filtering and Grouping in xslt for xml data

I have an xml content and i am applying xslt 1.0 for transformation on it. I am also passing parameters for filteration. but i am not able to to grouping on filtered data in xslt 1.0.
I will pass "Country Value" (as like 'United States') as parameter for filteration. after filteration, Grouping will be applied on "Group" field for filtered data. and if only one group exist then dont group data. grouping applied only in case if more then one group becomes possible.
please help me on this.
thanks in advance.
Here is my sample XML content.
<?xml version="1.0" encoding="utf-8" ?>
<DataRows>
-<DataRow>
- <Country>
<Conty>United States</Conty>
<Conty>United Kingdom</Conty>
</Country>
<Group>Group 1</Group>
<Order>1</Order>
<Name>Name 1_1</Name>
<Title>Title 1</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
<Conty>United Kingdom</Conty>
</Country>
<Group>Group 1</Group>
<Order>2</Order>
<Name>Name 2_2</Name>
<Title>Title 2</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
</Country>
<Group>Group 1</Group>
<Order>1</Order>
<Name>Name 3_1</Name>
<Title>Title 3</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
<Conty>Germany</Conty>
</Country>
<Group>Group 1</Group>
<Order>2</Order>
<Name>Name 4_2</Name>
<Title>Title 4</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
</Country>
<Group>Group 2</Group>
<Order>4</Order>
<Name>Name 8_4</Name>
<Title>Title 8</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United Kingdom</Conty>
</Country>
<Group>Group 2</Group>
<Order>1</Order>
<Name>Name 9_1</Name>
<Title>Title 9</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
<Conty>Germany</Conty>
</Country>
<Group>Group 2</Group>
<Order>3</Order>
<Name>Name 5_3</Name>
<Title>Title 5</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
<Conty>Germany</Conty>
</Country>
<Group>Group 2</Group>
<Order>4</Order>
<Name>Name 6_4</Name>
<Title>Title 6</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>United States</Conty>
</Country>
<Group>Group 2</Group>
<Order>3</Order>
<Name>Name 7_3</Name>
<Title>Title 7</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
-<DataRow>
- <Country>
<Conty>Germany</Conty>
</Country>
<Group>Group 1</Group>
<Order>1</Order>
<Name>Name 10_1</Name>
<Title>Title 10</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl />
<EmailId />
</DataRow>
</DataRows>
If you really wanted to do filtering first, then grouping, then you are looking at some sort of 'two pass' transform. This can be achieved by use of the node-set extension function, to create a result-tree fragment which contains the filtered data.
In the following example, I am using Microsoft's extension function, but depending on your platform, you may have to specify another. ( EXSLT is another common one. Use name space http://exslt.org/common for that. )
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="Conty">United Kingdom</xsl:param>
<xsl:variable name="FilteredData">
<xsl:apply-templates select="/DataRows/DataRow" mode="filter"/>
</xsl:variable>
<xsl:template match="DataRow" mode="filter">
<!-- Check this DataRow matches the filter -->
<xsl:if test="Country[Conty=$Conty]">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="filter"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<!-- Ignore Country node in the filter -->
<xsl:template match="Country" mode="filter"/>
<!-- Identity template for filter -->
<xsl:template match="#*|node()" mode="filter">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="filter"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/DataRows">
<xsl:copy>
<!-- Read the filtered data -->
<xsl:choose>
<!-- Check there is a Group which differs from the first group -->
<xsl:when test="msxsl:node-set($FilteredData)/DataRow[position() > 1][Group != msxsl:node-set($FilteredData)/DataRow[1]/Group]">
<xsl:apply-templates select="msxsl:node-set($FilteredData)"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="msxsl:node-set($FilteredData)" mode="nogroup"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- Filtered data row -->
<xsl:template match="DataRow">
<!-- Is this DataRow the first in the group -->
<xsl:if test="not(preceding-sibling::DataRow[Group=current()/Group])"><!-- If so, create the group node -->
<Group>
<xsl:attribute name="name">
<xsl:value-of select="Group"/>
</xsl:attribute><!-- Get all the DataRow elements from the filter for the current group -->
<xsl:apply-templates select="../DataRow[Group=current()/Group]" mode="ingroup"/>
</Group>
</xsl:if>
</xsl:template>
<!-- Identity template for the group -->
<xsl:template match="#*|node()" mode="ingroup">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="ingroup"/>
</xsl:copy>
</xsl:template>
<!-- Ignore Group and Country node in the grouping -->
<xsl:template match="Group|Country" mode="ingroup"/>
<!-- Identity template for no grouping -->
<xsl:template match="#*|node()" mode="nogroup">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="nogroup"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When this is used, the output should be as follows
<DataRows>
<Group name="Group 1">
<DataRow>
<Order>1</Order>
<Name>Name 1_1</Name>
<Title>Title 1</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl/>
<EmailId/>
</DataRow>
<DataRow>
<Order>2</Order>
<Name>Name 2_2</Name>
<Title>Title 2</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl/>
<EmailId/>
</DataRow>
</Group>
<Group name="Group 2">
<DataRow>
<Order>1</Order>
<Name>Name 9_1</Name>
<Title>Title 9</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl/>
<EmailId/>
</DataRow>
</Group>
</DataRows>
See Understanding the node-set function for more information.
Unless you did a two-phase transform, I think you should probably do the grouping first, and then the filtering.
Grouping would be achieved by the common Meunchain Grouping method. You first define a key to look up DataRow elements based on their Group
<xsl:key name="RowLookup" match="DataRow" use="Group"/>
And then, to get the unique group names, you match the DataRow elements which happen to be the first occuring element in your key for their particular group
<xsl:apply-templates select="DataRow[generate-id() = generate-id(key('RowLookup', Group)[1])]"/>
So, now you have grouped by the Group elements, so you need to check there it at least one DataRow element for the current group that matches the filter
<xsl:if test="../DataRow[Group=current()/Group]/Country[Conty=$Conty]">
And then to get all DataRow elements for the current group, you could use the key
<xsl:apply-templates select="key('RowLookup', Group)" mode="ingroup"/>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="Conty">United Kingdom</xsl:param>
<xsl:key name="RowLookup" match="DataRow" use="Group"/>
<xsl:template match="/DataRows">
<xsl:copy>
<!-- Select unique groups -->
<xsl:apply-templates
select="DataRow[generate-id() = generate-id(key('RowLookup', Group)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="DataRow">
<!-- Check any DataRow elements for the current group match the filter -->
<xsl:if test="../DataRow[Group=current()/Group]/Country[Conty=$Conty]">
<Group>
<xsl:attribute name="name">
<xsl:value-of select="Group"/>
</xsl:attribute>
<!-- Get all the DataRow elements for the current group -->
<xsl:apply-templates select="key('RowLookup', Group)" mode="ingroup"/>
</Group>
</xsl:if>
</xsl:template>
<xsl:template match="DataRow" mode="ingroup">
<!-- Check this DataRow matches the filter -->
<xsl:if test="Country[Conty=$Conty]">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<!-- Ignore Group and Country elements -->
<xsl:template match="Group|Country"/>
<!-- Standard Identity Transform for all other nodes -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When you apply this XSLT to your sample XML, you get the following results
<DataRows>
<Group name="Group 1">
<DataRow>
<Order>1</Order>
<Name>Name 1_1</Name>
<Title>Title 1</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl/>
<EmailId/>
</DataRow>
<DataRow>
<Order>2</Order>
<Name>Name 2_2</Name>
<Title>Title 2</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl/>
<EmailId/>
</DataRow>
</Group>
<Group name="Group 2">
<DataRow>
<Order>1</Order>
<Name>Name 9_1</Name>
<Title>Title 9</Title>
<PhoneNo>732-989-9898</PhoneNo>
<ImageUrl/>
<EmailId/>
</DataRow>
</Group>
</DataRows>
I am not sure if this is the exact structure you want, but I hope it gives you the general idea.

Is it possible in XSL to flatten XML hierarchy?

I have the following structure to XML file:
<INSTANCE>
<Sections>
<Section>
<Forms>
<Form>
<Control id="GroupHeading1">
<Property/>
<Property/>
</Control>
<Control id="GroupHeading2">
<Property/>
<Control id="TextBox">
<Property/>
<Property/>
</Control>
</Control>
</Form>
</Forms>
</Section>
</Sections>
</INSTANCE>
I am trying to deserialize this into C# object, but I don't need to preserve the hierarchy (which is making it difficult for me to deserialize).
Is there XSL that can transform this to un-nest the Controls, and if possible add an attribute to any child Control with ParentId=""?
Thank you for any guidance!
Given XML, the XmlSerializer can produce a graph of objects that hold the same instance data.
This is known as XML de-serialization
You need to look here :
Using the XmlSerializer Attributes
Serialization and Deserialization in ASP.NET with C#
This template should get you started. I ran it against .NET 2.0.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="Form">
<Form>
<xsl:copy-of select="#*"/>
<xsl:apply-templates select="//Control"/>
</Form>
</xsl:template>
<xsl:template match="Control">
<Control>
<xsl:if test="ancestor::Control/#id">
<xsl:attribute name="ParentID"><xsl:value-of select="ancestor::Control/#id"/></xsl:attribute>
</xsl:if>
<xsl:copy-of select="*|#*"/>
</Control>
</xsl:template>
</xsl:stylesheet>
This is the output (indented for readability).
<INSTANCE>
<Sections>
<Section>
<Forms>
<Form>
<Control id="GroupHeading1">
<Property />
<Property />
</Control>
<Control id="GroupHeading2">
<Property />
<Control id="TextBox">
<Property />
<Property />
</Control>
</Control>
<Control ParentID="GroupHeading2" id="TextBox">
<Property />
<Property />
</Control>
</Form>
</Forms>
</Section>
</Sections>
</INSTANCE>
The following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- first-level control elements -->
<xsl:template match="Control">
<Control>
<xsl:copy-of select="#*|*[not(self::Control)]" />
</Control>
<xsl:apply-templates select="Control" />
</xsl:template>
<!-- nested control elements -->
<xsl:template match="Control/Control">
<Control ParentId="{../#id}">
<xsl:copy-of select="#*|*[not(self::Control)]" />
</Control>
<xsl:apply-templates select="Control" />
</xsl:template>
</xsl:stylesheet>
Applied to the following document (same as original with one additional level of nesting for demonstration purposes):
<INSTANCE>
<Sections>
<Section>
<Forms>
<Form>
<Control id="GroupHeading1">
<Property />
<Property />
</Control>
<Control id="GroupHeading2">
<Property />
<Control id="TextBox">
<Property />
<Property />
<Control id="Grandchild">
<Property />
</Control>
</Control>
</Control>
</Form>
</Forms>
</Section>
</Sections>
</INSTANCE>
Produces an output with no nested <Control> elements:
<INSTANCE>
<Sections>
<Section>
<Forms>
<Form>
<Control id="GroupHeading1">
<Property />
<Property />
</Control>
<Control id="GroupHeading2">
<Property />
</Control>
<Control ParentId="GroupHeading2" id="TextBox">
<Property />
<Property />
</Control>
<Control ParentId="TextBox" id="Grandchild">
<Property />
</Control>
</Form>
</Forms>
</Section>
</Sections>
</INSTANCE>

Categories

Resources