I am beginner to xslt. I want to find all unique combinations for Year, Month, Day, and Hour.
I have following xml file to transform:
<Plans>
<Plan Name="Plan_1">
<Book Name="Book_1">
<Time Name="AAA">
<Property Name="Year" Value="2001"/>
<Property Name="Month" Value="01"/>
<Property Name="Day" Value="10"/>
<Property Name="Hour" Value="12"/>
</Time>
<Time Name="BBB">
<Property Name="Year" Value="2001"/>
<Property Name="Month" Value="01"/>
<Property Name="Day" Value="10"/>
<Property Name="Hour" Value="12"/>
</Time>
<Time Name="CCC">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="02"/>
<Property Name="Day" Value="11"/>
<Property Name="Hour" Value="04"/>
</Time>
<Time Name="DDD">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="20"/>
<Property Name="Hour" Value="04"/>
</Time>
</Book>
<Book Name="Book_22">
<Time Name="CCC">
<Property Name="Year" Value="2001"/>
<Property Name="Month" Value="01"/>
<Property Name="Day" Value="10"/>
<Property Name="Hour" Value="12"/>
</Time>
<Time Name="DDD">
<Property Name="Year" Value="2002"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="23"/>
<Property Name="Hour" Value="03"/>
</Time>
<Time Name="EEE">
<Property Name="Year" Value="2002"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="23"/>
<Property Name="Hour" Value="03"/>
</Time>
<Time Name="FFF">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="02"/>
<Property Name="Day" Value="11"/>
<Property Name="Hour" Value="04"/>
</Time>
</Book>
</Plan>
</Plans>
Input xml has total eight total combinations (Four from each book).
I just want to know distinct combinations. So output should have 6 combinations because there are two same combinations. The name of books does not matter. I just want to see how many combinations there are. And just label from 1 to the number... It does not have to be in any order although it can be ordered by time... If it is too hard to label numbers, then I just need to get distinct combinations only...
<Times>
<Time Value="1">
<Property Name="Year" Value="2001"/>
<Property Name="Month" Value="01"/>
<Property Name="Day" Value="10"/>
<Property Name="Hour" Value="12"/>
</Time>
<Time Value="2">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="02"/>
<Property Name="Day" Value="11"/>
<Property Name="Hour" Value="04"/>
</Time>
<Time Value="3">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="20"/>
<Property Name="Hour" Value="04"/>
</Time>
<Time Value="4">
<Property Name="Year" Value="2001"/>
<Property Name="Month" Value="01"/>
<Property Name="Day" Value="10"/>
<Property Name="Hour" Value="12"/>
</Time>
<Time Value="5">
<Property Name="Year" Value="2002"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="23"/>
<Property Name="Hour" Value="03"/>
</Time>
</Times>
I tried to read for-each-group. But I cannot make it work... Does it have to be four loops or something since there are four porperties? Please help me...
Similar to the other answers, you could also make use of xsl:for-each-group here.
<xsl:for-each-group select=".//Time" group-by="string-join(Property/#Value, '|')">
If your XSLT was using the Identity Template, you could then output the distinct Time elements within this group simply by doing this
<Time Value="{position()}">
<xsl:apply-templates />
</Time>
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<Times>
<xsl:for-each-group select=".//Time" group-by="string-join(Property/#Value, '|')">
<Time Value="{position()}">
<xsl:apply-templates />
</Time>
</xsl:for-each-group>
</Times>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Now, you mention in comments (but not in the question) about having multiple Plan elements, but you have not said if you want to select distinct Time elements across all Plan elements, or have distinct elements for each separate Plan.
Suppose you did want to show distinct Time elements for each separate Plan, then just a small tweak will do. You would just effectively match the Plan element and have the grouping in that
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="Plan">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select=".//Time" group-by="string-join(Property/#Value, '|')">
<Time Value="{position()}">
<xsl:apply-templates />
</Time>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Do note these assume the order of your Property elements will always be the same ("Year", "Month", "Day", "Hour"). If not, you can get around this by doing the following
<xsl:for-each-group select=".//Time" group-by="concat(
Property[#Name='Year']/#Value, '|',
Property[#Name='Month']/#Value, '|',
Property[#Name='Day']/#Value, '|',
Property[#Name='Hour']/#Value, '|')">
Use
distinct-values(/Times/Time/string-join(Property/#Value, '|'))
to get the distinct values. Then if necessary split them up into their component fields using tokenize().
The following stylesheet builds upon the answer given by #Michael Kay. This is just to illustrate the solution and prove that it is working. Please accept his answer!
Numbering output elements is not hard, the easiest way is using position() in an attribute value template.
<?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" indent="yes"/>
<xsl:template match="/Plans">
<Times>
<xsl:for-each select="distinct-values(//Time/string-join(Property/#Value, '|'))">
<xsl:variable name="tokens" select="tokenize(.,'\|')"/>
<Time Value="{position()}">
<Property Name="Year" Value="{$tokens[1]}"/>
<Property Name="Month" Value="{$tokens[2]}"/>
<Property Name="Day" Value="{$tokens[3]}"/>
<Property Name="Hour" Value="{$tokens[4]}"/>
</Time>
</xsl:for-each>
</Times>
</xsl:template>
</xsl:stylesheet>
Output
As mentioned by #Tim C, you are probably expecting 4 result elements.
<?xml version="1.0" encoding="UTF-8"?>
<Times>
<Time Value="1">
<Property Name="Year" Value="2001"/>
<Property Name="Month" Value="01"/>
<Property Name="Day" Value="10"/>
<Property Name="Hour" Value="12"/>
</Time>
<Time Value="2">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="02"/>
<Property Name="Day" Value="11"/>
<Property Name="Hour" Value="04"/>
</Time>
<Time Value="3">
<Property Name="Year" Value="2004"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="20"/>
<Property Name="Hour" Value="04"/>
</Time>
<Time Value="4">
<Property Name="Year" Value="2002"/>
<Property Name="Month" Value="03"/>
<Property Name="Day" Value="23"/>
<Property Name="Hour" Value="03"/>
</Time>
</Times>
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 days ago.
This post was edited and submitted for review 3 days ago.
Improve this question
NUnit testing produces a Results XML file that I would like to be able to create reports using Power Query with Power BI. I am really new to Power Query, and am really struggling with how to produce meaningful results. Namely, I would like to start with a report that shows all the tests with their respective pass/fail results for each test method that was run. I know how to do this with XPath, but I don't know if that is the Power Query way. I've search Google for some examples of this, but so far have found nothing helpful.
I have included a sample of NUnit V3 XML output. I basically want to transform all of the test-case elements as rows using Power Query within Power BI.
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<test-run id="2" name="mock-assembly.dll" fullname="D:\Dev\NUnit\nunit-3.0\work\bin\vs2008\Debug\mock-assembly.dll" testcasecount="25" result="Failed" time="0.154" total="18" passed="12" failed="2" inconclusive="1" skipped="3" asserts="2" run-date="2011-07-26" start-time="11:34:27">
<environment nunit-version="1.0.0.0" clr-version="2.0.50727.4961" os-version="Microsoft Windows NT 6.1.7600.0" platform="Win32NT" cwd="D:\Dev\NUnit\nunit-3.0\work\bin\vs2008\Debug" machine-name="CHARLIE-LAPTOP" user="charlie" user-domain="charlie-laptop" culture="en-US" uiculture="en-US" />
<test-suite type="Assembly" id="1036" name="mock-assembly.dll" fullname="D:\Dev\NUnit\nunit-3.0\work\bin\vs2008\Debug\mock-assembly.dll" testcasecount="25" result="Failed" time="0.154" total="18" passed="12" failed="2" inconclusive="1" skipped="3" asserts="2">
<properties>
<property name="_PID" value="11928" />
<property name="_APPDOMAIN" value="test-domain-mock-assembly.dll" />
</properties>
<failure>
<message><![CDATA[Child test failed]]></message>
</failure>
<test-suite type="TestFixture" id="1000" name="MockTestFixture" fullname="NUnit.Tests.Assemblies.MockTestFixture" testcasecount="11" result="Failed" time="0.119" total="10" passed="4" failed="2" inconclusive="1" skipped="3" asserts="0">
<properties>
<property name="Category" value="FixtureCategory" />
<property name="Description" value="Fake Test Fixture" />
</properties>
<failure>
<message><![CDATA[Child test failed]]></message>
</failure>
<test-case id="1005" name="FailingTest" fullname="NUnit.Tests.Assemblies.MockTestFixture.FailingTest" result="Failed" time="0.023" asserts="0">
<failure>
<message><![CDATA[Intentional failure]]></message>
<stack-trace><![CDATA[ at NUnit.Framework.Assert.Fail(String message, Object[] args) in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\framework\Assert.cs:line 142
at NUnit.Framework.Assert.Fail(String message) in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\framework\Assert.cs:line 152
at NUnit.Tests.Assemblies.MockTestFixture.FailingTest() in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\mock-assembly\MockAssembly.cs:line 121]]></stack-trace>
</failure>
</test-case>
<test-case id="1010" name="InconclusiveTest" fullname="NUnit.Tests.Assemblies.MockTestFixture.InconclusiveTest" result="Inconclusive" time="0.001" asserts="0" />
<test-case id="1001" name="MockTest1" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest1" result="Passed" time="0.000" asserts="0">
<properties>
<property name="Description" value="Mock Test #1" />
</properties>
</test-case>
<test-case id="1002" name="MockTest2" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest2" result="Passed" time="0.000" asserts="0">
<properties>
<property name="Severity" value="Critical" />
<property name="Description" value="This is a really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really long description" />
<property name="Category" value="MockCategory" />
</properties>
</test-case>
<test-case id="1003" name="MockTest3" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest3" result="Passed" time="0.000" asserts="0">
<properties>
<property name="Category" value="AnotherCategory" />
<property name="Category" value="MockCategory" />
</properties>
</test-case>
<test-case id="1007" name="MockTest4" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest4" result="Skipped" label="Ignored" time="0.000" asserts="0">
<properties>
<property name="Category" value="Foo" />
<property name="_SKIPREASON" value="ignoring this test method for now" />
</properties>
<reason>
<message><![CDATA[ignoring this test method for now]]></message>
</reason>
</test-case>
<test-case id="1004" name="MockTest5" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest5" result="Skipped" label="Invalid" time="0.000" asserts="0">
<properties>
<property name="_SKIPREASON" value="Method is not public" />
</properties>
<reason>
<message><![CDATA[Method is not public]]></message>
</reason>
</test-case>
<test-case id="1009" name="NotRunnableTest" fullname="NUnit.Tests.Assemblies.MockTestFixture.NotRunnableTest" result="Skipped" label="Invalid" time="0.000" asserts="0">
<properties>
<property name="_SKIPREASON" value="No arguments were provided" />
</properties>
<reason>
<message><![CDATA[No arguments were provided]]></message>
</reason>
</test-case>
<test-case id="1011" name="TestWithException" fullname="NUnit.Tests.Assemblies.MockTestFixture.TestWithException" result="Failed" label="Error" time="0.002" asserts="0">
<failure>
<message><![CDATA[System.ApplicationException : Intentional Exception]]></message>
<stack-trace><![CDATA[ at NUnit.Tests.Assemblies.MockTestFixture.MethodThrowsException() in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\mock-assembly\MockAssembly.cs:line 158
at NUnit.Tests.Assemblies.MockTestFixture.TestWithException() in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\mock-assembly\MockAssembly.cs:line 153]]></stack-trace>
</failure>
</test-case>
<test-case id="1006" name="TestWithManyProperties" fullname="NUnit.Tests.Assemblies.MockTestFixture.TestWithManyProperties" result="Passed" time="0.000" asserts="0">
<properties>
<property name="TargetMethod" value="SomeClassName" />
<property name="Size" value="5" />
</properties>
</test-case>
</test-suite>
<test-suite type="TestFixture" id="1023" name="BadFixture" fullname="NUnit.Tests.BadFixture" testcasecount="1" result="Skipped" label="Invalid" time="0.000" total="0" passed="0" failed="0" inconclusive="0" skipped="0" asserts="0">
<properties>
<property name="_SKIPREASON" value="No suitable constructor was found" />
</properties>
<reason>
<message><![CDATA[No suitable constructor was found]]></message>
</reason>
</test-suite>
<test-suite type="TestFixture" id="1025" name="FixtureWithTestCases" fullname="NUnit.Tests.FixtureWithTestCases" testcasecount="2" result="Passed" time="0.010" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="2">
<test-suite type="ParameterizedMethod" id="1026" name="MethodWithParameters" fullname="NUnit.Tests.FixtureWithTestCases.MethodWithParameters" testcasecount="2" result="Passed" time="0.009" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="2">
<test-case id="1027" name="MethodWithParameters(2,2)" fullname="NUnit.Tests.FixtureWithTestCases.MethodWithParameters(2,2)" result="Passed" time="0.006" asserts="1" />
<test-case id="1028" name="MethodWithParameters(9,11)" fullname="NUnit.Tests.FixtureWithTestCases.MethodWithParameters(9,11)" result="Passed" time="0.000" asserts="1" />
</test-suite>
</test-suite>
<test-suite type="TestFixture" id="1016" name="IgnoredFixture" fullname="NUnit.Tests.IgnoredFixture" testcasecount="3" result="Skipped" label="Ignored" time="0.000" total="0" passed="0" failed="0" inconclusive="0" skipped="0" asserts="0">
<properties>
<property name="_SKIPREASON" value="" />
</properties>
<reason>
<message><![CDATA[]]></message>
</reason>
</test-suite>
<test-suite type="ParameterizedFixture" id="1029" name="ParameterizedFixture" fullname="NUnit.Tests.ParameterizedFixture" testcasecount="4" result="Passed" time="0.007" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-suite type="TestFixture" id="1030" name="ParameterizedFixture(42)" fullname="NUnit.Tests.ParameterizedFixture(42)" testcasecount="2" result="Passed" time="0.003" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1031" name="Test1" fullname="NUnit.Tests.ParameterizedFixture(42).Test1" result="Passed" time="0.000" asserts="0" />
<test-case id="1032" name="Test2" fullname="NUnit.Tests.ParameterizedFixture(42).Test2" result="Passed" time="0.000" asserts="0" />
</test-suite>
<test-suite type="TestFixture" id="1033" name="ParameterizedFixture(5)" fullname="NUnit.Tests.ParameterizedFixture(5)" testcasecount="2" result="Passed" time="0.002" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1034" name="Test1" fullname="NUnit.Tests.ParameterizedFixture(5).Test1" result="Passed" time="0.000" asserts="0" />
<test-case id="1035" name="Test2" fullname="NUnit.Tests.ParameterizedFixture(5).Test2" result="Passed" time="0.000" asserts="0" />
</test-suite>
</test-suite>
<test-suite type="TestFixture" id="1012" name="OneTestCase" fullname="NUnit.Tests.Singletons.OneTestCase" testcasecount="1" result="Passed" time="0.001" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1013" name="TestCase" fullname="NUnit.Tests.Singletons.OneTestCase.TestCase" result="Passed" time="0.000" asserts="0" />
</test-suite>
<test-suite type="TestFixture" id="1014" name="MockTestFixture" fullname="NUnit.Tests.TestAssembly.MockTestFixture" testcasecount="1" result="Passed" time="0.001" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1015" name="MyTest" fullname="NUnit.Tests.TestAssembly.MockTestFixture.MyTest" result="Passed" time="0.001" asserts="0" />
</test-suite>
</test-suite>
</test-run>
I am trying to retrieve only distinct values of an element. This is my XML:
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cF">
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
<property key="MeterClassTypeCode" value="cF"/>
</LaunchedMeterClass>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cE">
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
<property key="MeterClassTypeCode" value="cE"/>
</LaunchedMeterClass>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cC">
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
<property key="MeterClassTypeCode" value="cC"/>
</LaunchedMeterClass>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cD">
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
<property key="MeterClassTypeCode" value="cD"/>
</LaunchedMeterClass>
I want to create an XPath to get only those "LaunchedMeterClass" nodes having an unique value for <property key="ClusterContractUUID">. At the moment, I am using the following:
<xsl:for-each select="./descendant::LaunchedMeterClass">
<xsl:choose>
<xsl:when test="./property/#key='ClusterContractUUID'">
<contract-type>
<property>
<xsl:attribute name="key">ClusterContractUUID</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="./property[#key='ClusterContractUUID']/#value"/>
</xsl:attribute>
</property>
</contract-type>
</xsl:when>
<xsl:otherwise>
<contract-type>
<property>
<xsl:attribute name="key">contractTypeCategory</xsl:attribute>
</property>
</contract-type>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
But this gives me all the elements including duplicates:
<contract-type>
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
</contract-type>
<contract-type>
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
</contract-type>
<contract-type>
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
</contract-type>
<contract-type>
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
</contract-type>
How can I write an XPath which gives only distinct values? My desired output is:
<contract-type>
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
</contract-type>
<contract-type>
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
</contract-type>
Thank you in advance for your help!
Try this:
XML:
<root>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cF">
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
<property key="MeterClassTypeCode" value="cF"/>
</LaunchedMeterClass>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cE">
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
<property key="MeterClassTypeCode" value="cE"/>
</LaunchedMeterClass>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cC">
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
<property key="MeterClassTypeCode" value="cC"/>
</LaunchedMeterClass>
<LaunchedMeterClass id="584e348b-2a06-42d0-a858-b8909f579238-St-4M-Template-Standard-cD">
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
<property key="MeterClassTypeCode" value="cD"/>
</LaunchedMeterClass>
</root>
XSLT2.0
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="root">
<xsl:for-each select="distinct-values(descendant::property[#key='ClusterContractUUID']/#value)">
<contract-type>
<property>
<xsl:attribute name="key">ClusterContractUUID</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="."/>
</xsl:attribute>
</property>
</contract-type>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<contract-type>
<property key="ClusterContractUUID" value="c2cebd90-9265-4cea-8018-0aac6efcced2"/>
</contract-type>
<contract-type>
<property key="ClusterContractUUID" value="d0c9f440-172c-49ad-9b95-cddce23f16fa"/>
</contract-type>
</root>
Edit:
Edited to get following-sibling values. In above XSLT, template match is # attributes side, instead of that if template match at elements, we can access the other siblings, ancestors, descendants, etc, easily.
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#* | node()"/></xsl:copy>
</xsl:template>
<xsl:template match="root">
<root>
<xsl:for-each select="descendant::property[#key='ClusterContractUUID'
and not(#value=preceding::property[#key='ClusterContractUUID']/#value)]">
<contract-type>
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
<xsl:apply-templates select="following-sibling::property"/>
</contract-type>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
You can perhaps try exslt's set:distinct:
http://exslt.org/set/functions/distinct/
There's an example at the bottom. Your mileage may vary.
This is generally known as "grouping".
In XSLT 2.0, use the <xsl:for-each-group> construct.
In XSLT 1.0, use "Muenchian grouping" - you'll find that in your favourite XSLT textbook, or in online resources.
If you really need to do it in XPath rather than XSLT (your question is unclear) the best you can do is the XPath 2.0 distinct-values() function - there's nothing in XPath 1.0 that will help you much.
My template_1.xml file
<?xml version="1.0" encoding="UTF-8"?>
<DSExport>
<TableDefinitions>
<Property Name="Category">\Table Definitions\Teradata\XML_TEST</Property>
<Property Name="ShortDesc">Imported from: SRC_COLUMN_ADD_TEST</Property>
<Collection Name="Columns" Type="MetaColumn">
<SubRecord>
<Property Name="Name">CUST_ID_1</Property>
<Property Name="Description">CUST_ID: nullable int32</Property>
<Property Name="SqlType">4</Property>
<Property Name="Precision">9</Property>
<Property Name="Scale">0</Property>
<Property Name="Nullable">1</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">DESCR</Property>
<Property Name="Description">DESCR: nullable string[max=144]</Property>
<Property Name="SqlType">12</Property>
<Property Name="Precision">144</Property>
<Property Name="Scale">0</Property>
<Property Name="Nullable">1</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">CUST_ADDR</Property>
<Property Name="Description">CUST_ADDR: string[max=500]</Property>
<Property Name="SqlType">12</Property>
<Property Name="Precision">500</Property>
<Property Name="Scale">0</Property>
<Property Name="Nullable">0</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">AGE</Property>
<Property Name="Description">AGE: nullable int32</Property>
<Property Name="SqlType">4</Property>
<Property Name="Precision">9</Property>
<Property Name="Scale">0</Property>
<Property Name="Nullable">1</Property>
</SubRecord>
</Collection>
Hi I am new to XSLT , I tried lot to get my expected out as mentioned below, some how am getting result only from same attribute value of same element, please help on this...Thanks
My template.xsl file
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<DSExport>
<id-of>
<xsl:for-each select="//SubRecord">
<SubRecord>
<xsl:value-of select="//SubRecord/Property[#Name]" />
</SubRecord>
</xsl:for-each>
</id-of>
</DSExport>
</xsl:template>
</xsl:stylesheet>
My current ouput: output.xml
<?xml version="1.0" encoding="UTF-8"?>
<DSExport>
<id-of>
<SubRecord>CUST_ID_1</SubRecord>
<SubRecord>CUST_ID_1</SubRecord>
<SubRecord>CUST_ID_1</SubRecord>
<SubRecord>CUST_ID_1</SubRecord>
</id-of>
</DSExport>
My expected output :
<?xml version="1.0" encoding="UTF-8"?>
<DSExport>
<id-of>
<SubRecord>CUST_ID_1</SubRecord>
<SubRecord>DESCR</SubRecord>
<SubRecord>CUST_ADDR</SubRecord>
<SubRecord>AGE</SubRecord>
</id-of>
</DSExport>
Your problem is with this expression
<xsl:value-of select="//SubRecord/Property[#Name]" />
The first slash at the start of the xpath expression indicates it is an absolute path, and so it will start searching from the document element. Two slashes then indicate it will search for the SubRecord element at any level in the document. This results in it always finding the first SubRecord element in the XML, regardless of where you are currently positioned.
You need to use a relative expression here. It will be relative to the context node you are currently positioned on (which is a SubRecord element). Try replacing it with this
<xsl:value-of select="Property[#Name]" />
Note that, strictly speaking this will get the first Property element which has an attribute of Name present. Although this gives your expected result, maybe it would be better written as this
<xsl:value-of select="Property[#Name='Name']" />
i.e. Get the Property which has a Name attribute with a value of Name.
I am getting this error while I am trying to test the data service on the wso2 dss
DS Fault Message: Error in
'SQLQuery.processStoredProcQuery'DS Code: DATABASE_ERRORSource Data
Service:-Name: CustomerDSLocation: \CustomerDS-1.0.0.dbsDescription:
N/ADefault Namespace: http://ws.wso2.org/dataserviceCurrent Request
Name: op1Current Params: {Name=?, NID=?}Nested Exception:-DS Fault
Message: Error in 'createProcessedPreparedStatement'DS Code:
UNKNOWN_ERRORNested Exception:-java.sql.SQLException: Parameter index
of 3 is out of range (1, 0)
any suggestions?
edited: The bds file is as follows, this is a sample case mentioned in the wso2 documentation.
<data name="CustomerDS">
<config id="default">
<property name="org.wso2.ws.dataservice.driver">com.mysql.jdbc.Driver</property>
<property name="org.wso2.ws.dataservice.protocol">jdbc:mysql://localhost:3306/CustomersDatabase</property>
<property name="org.wso2.ws.dataservice.user">root</property>
<property name="org.wso2.ws.dataservice.password">root</property>
</config>
<query id="q1" useConfig="default">
<sql>call getCustomer(?,?,?,?)</sql>
<result element="Entries" rowName="Entry">
<element name="Flag" column="Flag" xsdType="xs:integer" optional="true" />
<element name="Customer" column="Customer" xsdType="xs:string" optional="true" />
</result>
<param name="NID" sqlType="STRING" ordinal="1" />
<param name="Name" sqlType="STRING" ordinal="2" />
<param name="Flag" sqlType="INTEGER" type="OUT" ordinal="3" />
<param name="Customer" sqlType="STRING" type="OUT" ordinal="4" />
</query>
<operation name="op1">
<call-query href="q1">
<with-param name="NID" query-param="NID" />
<with-param name="Name" query-param="Name" />
</call-query>
</operation>
</data>
WHAT WORKED FOR ME!!
I found the reference code at http://svn.wso2.org/repos/wso2/people/kasun/wso2con_2013/starbucks_2.0/dss/StarbucksDataService.dbs
and the procedure call that is finally working for me is (which changes in procedure itself)
<data name="CustomerDS">
<config id="default">
<property name="org.wso2.ws.dataservice.driver">com.mysql.jdbc.Driver</property>
<property name="org.wso2.ws.dataservice.protocol">jdbc:mysql://localhost:3306/CustomersDatabase</property>
<property name="org.wso2.ws.dataservice.user">root</property>
<property name="org.wso2.ws.dataservice.password">root</property>
</config>
<query id="q1" useConfig="default">
<sql>call getCustomer(?,?)</sql>
<result element="Entries" rowName="Entry">
<element name="Flag" column="Flag" xsdType="xs:integer" optional="true" />
<element name="Customer" column="Customer" xsdType="xs:string" optional="true" />
</result>
<param name="NID" sqlType="STRING" ordinal="1" />
<param name="Name" sqlType="STRING" ordinal="2" />
<param name="Flag" sqlType="INTEGER" type="OUT" ordinal="3" />
<param name="Customer" sqlType="STRING" type="OUT" ordinal="4" />
</query>
<operation name="op1">
<call-query href="q1">
<with-param name="NID" query-param="NID" />
<with-param name="Name" query-param="Name" />
</call-query>
</operation>
</data>
And the tentative working procedure call for now is :
DELIMITER //
CREATE procedure getCustomer(NID varchar(200),Name varchar(200))
BEGIN
DECLARE id varchar(200);
DECLARE flag int;
SET Flag = 0;
SET id = CONCAT(NID, '_' , Name);
INSERT INTO Customer(NID, Name, customerID) VALUES(NID, Name, id);
select flag, customerid from customer where customerID = id;
END//
In the query q1 call getCustomer(?,?,?,?) takes 4 arguments and in the input mapping list there are only 2 input params (IN Only). This must be the reason for the this exception. Please refer this tutorial.
I'm trying to transform my xml file:
<root>
<group id="F_123" >
<term id="F_123_d" >
<word>blabla</word>
<instruction>blabla</instruction>
</term>
<term id="F_123">
<word>blabla</word>
<instruction>blabla</instruction>
<numbers>
<number code="01" >1</number>
<number code="02" >2</number>
<number code="03" >3</number>
<number code="04" >4</number>
<number code="05" >5</number>
</numbers>
</term>
<term id="F_124">
<word>blabla</word>
<numbers>
<number code="01" >1</number>
<number code="02" >2</number>
<number code="03" >3</number>
<number code="04" >4</number>
<number code="05" >5</number>
</numbers>
</term>
<term id="F_125">
<word>blabla</word>
<numbers>
<number code="01" >1</number>
<number code="02" >2</number>
<number code="03" >3</number>
<number code="04" >4</number>
<number code="05" >5</number>
</numbers>
</term>
<routing id="F_123_1">
<condition>
<operator type="or">
<operator type="or">
<operator type="equal">
<variable name="F_D01a3DE1"/>
<constant>DK</constant>
</operator>
<operator type="equal">
<variable name="F_D01a3DE1"/>
<constant>RF</constant>
</operator>
</operator>
<operator type="equal">
<variable name="F_D01a3DE1"/>
<constant>1</constant>
</operator>
</operator>
</condition>
<then>
<goto group="A_24"/>
</then>
<else>
<routing>
<condition>
<operator type="or">
<operator type="or">
<operator type="equal">
<variable name="B_D01a3DE1"/>
<constant>5</constant>
</operator>
<operator type="equal">
<variable name="B_D01a3DE1"/>
<constant>10</constant>
</operator>
</operator>
<operator type="equal">
<variable name="B_D01a3DE1"/>
<constant>7</constant>
</operator>
</operator>
</condition>
<then>
<goto group="A_25"/>
</then>
<else>
<routing>
<condition>
<operator type="or">
<operator type="equal">
<variable name="B_D01a3DE1"/>
<constant>6</constant>
</operator>
<operator type="equal">
<variable name="B_D01a3DE1"/>
<constant>11</constant>
</operator>
</operator>
</condition>
<then>
<goto group="A_26"/>
</then>
<else>
<goto group="A_27"/>
</else>
</routing>
</else>
</routing>
</else>
</routing>
</group>
<group id="A_25" >
<term id="A_25" >
<word>blabla</word>
<instruction>blabla</instruction>
</term>
<term id="A_26">
<word>blabla</word>
<instruction>blabla</instruction>
<numbers>
<number code="01" >1</number>
<number code="02" >2</number>
</numbers>
</term>
</group>
</root>
I want to access the value of #group/term/#id and make one element per each term in <group id="A_25">. Is it possible?
It's not very clear what exactly do you want, so a guess follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="group/term/#id">
<id>
<xsl:value-of select="."/>
</id>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:apply-templates select="node()" />
</root>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:apply-templates select="node()|#*" />
</xsl:template>
</xsl:stylesheet>
When applied to your example (I had to add a root element to it, so that it's valid), it produces:
<root>
<id>F_123_d</id>
<id>F_123</id>
<id>F_124</id>
<id>F_125</id>
<id>A_25</id>
<id>A_26</id>
</root>
I want to access the value of
#group/term/#id and make one element
per each term in <group id="A_25">. Is
it possible?
This XPath expression select what I think you want:
/root/group[#id='A_25']/term/#id
Also, this stylesheet process what I think you want:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="text()"/>
<xsl:template match="group[#id='A_25']/term">
<element id="{#id}"/>
</xsl:template>
</xsl:stylesheet>
Output:
<element id="A_25" />
<element id="A_26" />